136

Is there a more compact or pythonic way to write the boolean expression

a + b == c or a + c == b or b + c == a

I came up with

a + b + c in (2*a, 2*b, 2*c)

but that is a little strange.

14

16 Answers 16

206

If we look at the Zen of Python, emphasis mine:

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

The most Pythonic solution is the one that is clearest, simplest, and easiest to explain:

a + b == c or a + c == b or b + c == a

Even better, you don't even need to know Python to understand this code! It's that easy. This is, without reservation, the best solution. Anything else is intellectual masturbation.

Furthermore, this is likely the best performing solution as well, as it is the only one out of all the proposals that short circuits. If a + b == c, only a single addition and comparison is done.

5
  • 12
    Even better, throw in some parenthesis to make the intent crystal clear. Commented Aug 21, 2015 at 0:58
  • 3
    The intent is already crystal clear without parentheses. Parentheses would make it harder to read - why is the author using parentheses when precedence already covers this?
    – mrr
    Commented Aug 25, 2015 at 21:33
  • 1
    One other note about trying to be too clever: you may introduce unforeseen bugs by missing conditions you didn't consider. In other words, you may think your new compact solution is equivalent, but it isn't in all cases. Unless there is a compelling reason to code otherwise (performance, memory constraints, and so on), clarity is king.
    – Rob Craig
    Commented Aug 25, 2015 at 23:27
  • It depends on what the formula is for. Look at 'Explicit is better than implicit', it might be that the 'sorting first' approach more clearly expresses what the program is doing, or one of the others. I don't think we can judge from the question. Commented Aug 30, 2015 at 23:20
  • "Anything else is intellectual masturbation." Never heard that phrase before, not sure if looking it up is a good idea :P
    – 303
    Commented Nov 24, 2022 at 1:26
101

Solving the three equalities for a:

a in (b+c, b-c, c-b)
7
  • 4
    The only problem with this is the side effects. If b or c are more complex expressions, they'll be run multiple times. Commented Aug 19, 2015 at 18:21
  • 3
    @Kroltan My point was that I actually answered his question, which asked for a "more compact" representation. See: en.m.wikipedia.org/wiki/Short-circuit_evaluation
    – Alex Varga
    Commented Aug 20, 2015 at 11:18
  • 26
    Anyone reading this code will probably curse you for being "clever". Commented Aug 20, 2015 at 13:24
  • 5
    @SilvioMayolo The same is true of the original
    – Izkata
    Commented Aug 20, 2015 at 15:59
  • 1
    @AlexVarga, "My point was that I actually answered his question". You did; it uses 30% fewer characters (putting spaces between operators). I wasn't trying to say your answer was wrong, just commenting on how idiomatic (pythonic) it is. Nice answer. Commented Aug 21, 2015 at 4:49
54

Python has an any function that does an or on all the elements of a sequence. Here I've converted your statement into a 3-element tuple.

any((a + b == c, a + c == b, b + c == a))

Note that or is short circuiting, so if calculating the individual conditions is expensive it might be better to keep your original construct.

10
  • 2
    any() and all() short-circuit too. Commented Aug 19, 2015 at 4:26
  • 45
    @TigerhawkT3 Not in this case though; the three expressions will be evaluated before the tuple exists, and the tuple will exist before any even runs.
    – poke
    Commented Aug 19, 2015 at 4:41
  • 13
    Ah, I see. I guess it's only when there's a generator or similar lazy iterator in there. Commented Aug 19, 2015 at 4:45
  • 5
    any and all "short-circuit" the process of examining the iterable they're given; but if that iterable is a sequence rather than a generator, then it has been already been fully evaluated before the function call occurs. Commented Aug 20, 2015 at 11:42
  • This has the advantage that it's easy to split over multiple lines (double-indent the arguments to any, single-indent the ): in the if statement), which helps plenty for readability when math is involved
    – Izkata
    Commented Aug 20, 2015 at 16:03
40

If you know you're only dealing with positive numbers, this will work, and is pretty clean:

a, b, c = sorted((a, b, c))
if a + b == c:
    do_stuff()

As I said, this only works for positive numbers; but if you know they're going to be positive, this is a very readable solution IMO, even directly in the code as opposed to in a function.

You could do this, which might do a bit of repeated computation; but you didn't specify performance as your goal:

from itertools import permutations

if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
    do_stuff()

Or without permutations() and the possibility of repeated computations:

if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
    do_stuff()

I would probably put this, or any other solution, into a function. Then you can just cleanly call the function in your code.

Personally, unless I needed more flexibility from the code, I would just use the first method in your question. It's simple and efficient. I still might put it into a function:

def two_add_to_third(a, b, c):
    return a + b == c or a + c == b or b + c == a

if two_add_to_third(a, b, c):
    do_stuff()

That's pretty Pythonic, and it's quite possibly the most efficient way to do it (the extra function call aside); although you shouldn't worry too much about performance anyway, unless it's actually causing an issue.

5
  • especially if we can assume a, b, c are all non-negative.
    – cphlewis
    Commented Aug 19, 2015 at 2:54
  • I find the phrase "doesn't always work" a little confusing. The first solution only works if you know for sure that your numbers are non-negative. For instance with (a, b, c) = (-3, -2, -1) you have a+b != c but b+c=a. Similar cases with (-1, 1, 2) and (-2, -1, 1).
    – usernumber
    Commented Aug 19, 2015 at 13:16
  • @usernumber, you know, I noticed that earlier; not sure why I didn't fix it.
    – Cyphase
    Commented Aug 19, 2015 at 14:40
  • Your top solution doesn't work for a large class of inputs, whereas the OP's suggestion works for all inputs. How is "not working" more Pythonic than "working"?
    – Barry
    Commented Aug 19, 2015 at 17:04
  • 3
    Ooh, snap. "If you know you're only dealing with positive numbers, this will work, and is pretty clean". All the others work for any numbers, but if you know you're only dealing with positive numbers, the top one is very readable/Pythonic IMO.
    – Cyphase
    Commented Aug 19, 2015 at 17:07
16

If you will only be using three variables then your initial method:

a + b == c or a + c == b or b + c == a

Is already very pythonic.

If you plan on using more variables then your method of reasoning with:

a + b + c in (2*a, 2*b, 2*c)

Is very smart but lets think about why. Why does this work?
Well through some simple arithmetic we see that:

a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c

And this will have to hold true for either a,b, or c, meaning that yes it will equal 2*a, 2*b, or 2*c. This will be true for any number of variables.

So a good way to write this quickly would be to simply have a list of your variables and check their sum against a list of the doubled values.

values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])

This way, to add more variables into the equation all you have to do is edit your values list by 'n' new variables, not write 'n' equations

12
  • 4
    What about a=-1, b=-1, c=-2, then a+b=c, but a+b+c = -4 and 2*max(a,b,c) is -2 Commented Aug 19, 2015 at 3:10
  • Thank you that is true, I would need to use abs. Making that adjustment now. Commented Aug 19, 2015 at 3:14
  • 2
    After peppering it with half a dozen abs() calls, it's Pythonic than the OP's snippet (I'd actually call it significantly less readable). Commented Aug 19, 2015 at 3:24
  • That is very true, I will adjust that now Commented Aug 19, 2015 at 3:24
  • 1
    @ThatGuyRussell In order to short circuit, you would want to use a generator... something like any(sum(values) == 2*x for x in values), that way you wouldn't have to do all the doubling up front, just as necessary.
    – Barry
    Commented Aug 20, 2015 at 13:11
12

The following code can be used to iteratively compare each element with the sum of the others, which is computed from sum of the whole list, excluding that element.

 l = [a,b,c]
 any(sum(l)-e == e for e in l)
3
  • 2
    Nice :) I think if you remove the [] brackets from the second line, this will even short-circuit like the original with or...
    – psmears
    Commented Aug 19, 2015 at 18:00
  • 1
    which is basically any(a + b + c == 2*x for x in [a, b, c]), quite close to the OP's suggestion
    – njzk2
    Commented Aug 20, 2015 at 0:04
  • That is similar, however this method extends to any number of variables. I incorporated the suggestion by @psmears about short-circuiting.
    – Arcanum
    Commented Oct 7, 2015 at 4:27
10

Don't try and simplify it. Instead, name what you're doing with a function:

def any_two_sum_to_third(a, b, c):
  return a + b == c or a + c == b or b + c == a

if any_two_sum_to_third(foo, bar, baz):
  ...

Replace the condition with something "clever" might make it shorter, but it won't make it more readable. Leaving it how it is isn't very readable either however, because it's tricky to know why you're checking those three conditions at a glance. This makes it absolutely crystal clear what you're checking for.

Regarding performance, this approach does add the overhead of a function call, but never sacrifice readability for performance unless you've found a bottleneck you absolutely must fix. And always measure, as some clever implementations are capable of optimizing away and inlining some function calls in some circumstances.

3
  • 1
    Functions should be written only if you expect to use the same code in more than one spot or if the code is complex. There is no mention of code reuse in the original question, and writing a function for a single line of code is not only overkill but it actually impairs readability. Commented Aug 24, 2015 at 8:06
  • 5
    Coming from the FP school of things, I pretty much have to disagree completely and state that well named one-line functions are some of the best tools for increasing readability you'll ever find. Make a function whenever the steps you're taking to do something do not immediately bring clarity to what you're doing, as the name of the function lets you specify the what better than any comment could.
    – Jack
    Commented Aug 24, 2015 at 16:10
  • 1
    Whatever school you invoke it is bad to blindly adhere to a set of rules. Having to jump to another part of the source to read that one line of code hidden inside a function just to be able to verify that it actually does what it says in the name, and then having to switch back to the place of a call to make sure you are passing correct parameters is completely unnecessary context switching. In my opinion doing that impairs both the readability and the workflow. Finally, neither the name of a function nor the code comments are a proper replacement for code documentation. Commented Aug 26, 2015 at 14:07
9

Python 3:

(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...

It scales to any number of variables:

arr = [a,b,c,d,...]
sum(arr)/2 in arr

However, in general I agree that unless you have more than three variables, the original version is more readable.

7
  • 3
    This returns incorrect results for some inputs because of floating point rounding errors.
    – pts
    Commented Aug 21, 2015 at 15:53
  • Division should be avoided for performance and accuracy reasons. Commented Aug 24, 2015 at 8:24
  • 1
    @pts Will not any implementation return incorrect results because of floating point rounding? Even a+b==c
    – osundblad
    Commented Aug 26, 2015 at 15:45
  • @osundblad: If a, b and c are ints, then (a+b+c)/2 does rounding (and may return incorrect results), but a+b==c is accurate.
    – pts
    Commented Aug 26, 2015 at 20:00
  • 3
    division by 2 is simply decreasing the exponent by one, so it will be accurate for any integer that is less than 2^53 (the fraction part of a float in python), and for larger integers you can use decimal. For example, to check integers that are less than 2^30 run [x for x in range(pow(2,30)) if x != ((x * 2)/ pow(2,1))] Commented Aug 27, 2015 at 2:36
6
(a+b-c)*(a+c-b)*(b+c-a) == 0

If the sum of any two terms is equal to the third term, then one of the factors will be zero, making the entire product zero.

6
  • I was thinking exactly the same thing but I can't deny that his original proposal is way cleaner...
    – user541686
    Commented Aug 19, 2015 at 17:48
  • @Mehrdad - Definitely. It's really no different than (a+b<>c) && (a+c<>b) && (b+c<>a) == false
    – mbeckish
    Commented Aug 19, 2015 at 19:10
  • It is just that multiplication is more expensive than the logical expressions and basic arithmetic. Commented Aug 20, 2015 at 9:57
  • @IgorLevicki - Yes, although that's a VERY premature optimization concern. Is this going to be performed tens of thousands of times per second? If yes, then you would probably want to look at something else.
    – mbeckish
    Commented Aug 20, 2015 at 13:09
  • @mbeckish - Why do you think it is premature? Code should be written with optimization in mind, not optimized as an after-thought. One day some intern will copy this code snippet and paste it into some performance critical loop on an embedded platform which will then run on millions of devices not necessarily being slow for what it does, but perhaps wasting more battery power. Writing such code just encourages bad coding practices. In my opinion, what OP should have asked is whether there a way to optimize that logic expression. Commented Aug 23, 2015 at 10:07
6

How about just:

a == b + c or abs(a) == abs(b - c)

Note that this won't work if variables are unsigned.

From the viewpoint of code optimization (at least on x86 platform) this seems to be the most efficient solution.

Modern compilers will inline both abs() function calls and avoid sign testing and subsequent conditional branch by using a clever sequence of CDQ, XOR, and SUB instructions. The above high-level code will thus be represented with only low-latency, high-throughput ALU instructions and just two conditionals.

1
  • And I think fabs() can be used for float types ;).
    – shA.t
    Commented Aug 26, 2015 at 8:24
4

The solution provided by Alex Varga "a in (b+c, b-c, c-b)" is compact and mathematically beautiful, but I wouldn't actually write code that way because the next developer coming along would not immediately understand the purpose of the code.

Mark Ransom's solution of

any((a + b == c, a + c == b, b + c == a))

is more clear but not much more succinct than

a + b == c or a + c == b or b + c == a

When writing code that someone else will have to look at, or that I will have to look at a long time later when I have forgotten what I was thinking when I wrote it, being too short or clever tends to do more harm than good. Code should be readable. So succinct is good, but not so succinct that the next programmer can't understand it.

2
  • Honest question: why do people always assume that the next programmer will be an idiot incapable of reading code? I personally find that idea insulting. If code must be written to be blatantly obvious to every programmer, then that implies that we as a profession are catering to the lowest common denominator, the least skilled among us. If we keep doing that, how are they ever going to improve their personal skill? I don't see this in other professions. When was the last time you saw a composer writing a simple music score just so that every musician can play it regardless of skill level? Commented Aug 24, 2015 at 8:23
  • 6
    The issue is that even programmers have limited mental energy, so do you want to spend your limited mental energy on the algorithm and higher level aspects of the program, or on figuring out what some complicated line of code means when it can be expressed more simply? Programming is hard, so don't make it harder on yourself unnecessarily, just as an Olympic runner would not run a race with a heavy backpack on just because they can. As Steve McConell says in Code Complete 2, readability is one of the most important aspects of code. Commented Aug 24, 2015 at 12:17
2

Request is for more compact OR more pythonic - I tried my hand at more compact.

given

import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
    return x + y == z

This is 2 characters less than the original

any(g(*args) for args in f((a,b,c)))

test with:

assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)

additionally, given:

h = functools.partial(itertools.starmap, g)

This is equivalent

any(h(f((a,b,c))))
5
  • Well, it's two characters shorter than the original, but not the one the OP gave right afterward, which he said he's currently using. The original also includes lots of whitespace, which this omits whenever possible. There's also the small matter of the function g() you have to define for this to work. Given all that, I'd say it's significantly larger. Commented Aug 19, 2015 at 4:14
  • @TigerhawkT3, I interpreted it as a request for a shorter expression/line. see edit for further improvement.
    – wwii
    Commented Aug 19, 2015 at 4:28
  • 4
    Very bad function names, only suitable for a code golf.
    – 0xc0de
    Commented Aug 20, 2015 at 5:23
  • @0xc0de - sorry I don't play. Suitable can be pretty subjective and dependent on circumstances - but I will defer to the community.
    – wwii
    Commented Aug 22, 2015 at 14:21
  • I don't see how is this more compact when it has more characters than the original code. Commented Aug 24, 2015 at 8:10
1

As an old habit of my programming, I think placing complex expression at right in a clause can make it more readable like this:

a == b+c or b == a+c or c == a+b

Plus ():

((a == b+c) or (b == a+c) or (c == a+b))

And also I think using multi-lines can also make more senses like this:

((a == b+c) or 
 (b == a+c) or 
 (c == a+b))
0

In a generic way,

m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();

if, manipulating an input variable is OK for you,

c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();

if you want to exploit using bit hacks, you can use "!", ">> 1" and "<< 1"

I avoided division though it enables use to avoid two multiplications to avoid round off errors. However, check for overflows

0
def any_sum_of_others (*nums):
    num_elements = len(nums)
    for i in range(num_elements):
        discriminating_map = map(lambda j: -1 if j == i else 1, range(num_elements))
        if sum(n * u for n, u in zip(nums, discriminating_map)) == 0:
            return True
    return False

print(any_sum_of_others(0, 0, 0)) # True
print(any_sum_of_others(1, 2, 3)) # True
print(any_sum_of_others(7, 12, 5)) # True
print(any_sum_of_others(4, 2, 2)) # True
print(any_sum_of_others(1, -1, 0)) # True
print(any_sum_of_others(9, 8, -4)) # False
print(any_sum_of_others(4, 3, 2)) # False
print(any_sum_of_others(1, 1, 1, 1, 4)) # True
print(any_sum_of_others(0)) # True
print(any_sum_of_others(1)) # False
2
  • Functions should be written only if you expect to use the same code in more than one spot or if the code is complex. There is no mention of code reuse in the original question, and writing a function for a single line of code is not only overkill but it actually impairs readability. Commented Aug 24, 2015 at 8:06
  • I disagree that it impairs readability; if you choose a suitable name it may improve readability (but I make no representation as to the quality of the name I chose in this answer). In addition, it can be helpful to give a name to a concept, which you will have to do as long as you force yourself to give a good name to your function. Functions are good. As to whether the functionality is complex enough to benefit from being encapsulated in a function, that's a subjective judgement.
    – Hammerite
    Commented Aug 24, 2015 at 10:44
0

There is little to gain with such a small expression but using a function just to not having to repeat the summation and comparison could be an option. It makes it a bit more maintainable when wanting to change the operation to something like a + b == c * 2.

def equals_sum(a, b, c):
    return a + b == c

if (equals_sum(a, b, c)
or equals_sum(a, c, b)
or equals_sum(b, c, a)):
    ...

Not the answer you're looking for? Browse other questions tagged or ask your own question.