1

I came across this implementation of gcd in Python:

def gcd(x,y): return y and gcd(y, x % y) or x

What I don't understand is how the boolean is working in the return? After trying some numbers in the interpreter I noticed that and always returns the number on the right, while or returns the number on the left. Why is this? Also, can you walk me step by step through a simple call of this function so I can understand what is happening?

b_pcakes
  • 2,102
  • 3
  • 20
  • 37
  • 1
    possible duplicate of [Does Python support short-circuiting?](http://stackoverflow.com/questions/2580136/does-python-support-short-circuiting) – TigerhawkT3 Aug 22 '15 at 03:22
  • 3
    This could be rewritten as `def gcd(x, y): return gcd(y, x % y) if y else x`. See http://stackoverflow.com/questions/394809/does-python-have-a-ternary-conditional-operator. – deltab Aug 22 '15 at 03:27
  • One other thing to point out is that 'and' has greater precedence than 'or' which is why when 'y and gcd(y, x % y)' is False, x is returned. See https://docs.python.org/2/reference/expressions.html#operator-precedence. –  Aug 22 '15 at 03:51

4 Answers4

5

This is because of how and and or operators evaluate in Python.

From documentation -

The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.

The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.

They do not return True or False , they return the last evaluated value , and that is why we can write things like -

s = s or "Some default value"

To default the value of s if its None or empty string or empty list, or 0.


Basically, or returns the first non false-like value ( where false-like values are 0, or None or Empty string/list/tuple, etc ) Or the last false-like value if all the values are false-like. Example -

In [1]: 0 or 10
Out[1]: 10

In [2]: 5 or 0 or 10
Out[2]: 5

In [7]: 0 or '' or [] or ()
Out[7]: ()

And, and returns the first false-like value, or the last true-like value , if all the values are true-like. Example -

In [3]: 0 and 10
Out[3]: 0

In [4]: 5 and 10
Out[4]: 10

In [6]: 5 and 0 and 10
Out[6]: 0

In your case, it works as -

  1. if y is 0 it returns x (irrespective of the value of x) .

  2. otherwise it computes gcd(y, x%y) if that is non-zero returns it. (Though it would never really be 0)

  3. if the result of gcd(y, x%y) is 0, then it returns x .

Community
  • 1
  • 1
Anand S Kumar
  • 76,986
  • 16
  • 159
  • 156
  • @AnandSKumar In this example, gcd(y, x%y) would never be zero thought right? since the only results that are ever really returned are either y if non-zero or x, which is always non-zero? – b_pcakes Aug 22 '15 at 04:09
  • Correct it would never be 0 , I just put it there for the explanation of the boolean operators. – Anand S Kumar Aug 22 '15 at 04:09
  • Right, that's what I thought, just checking. Thanks – b_pcakes Aug 22 '15 at 04:10
1

This is called "short circuiting." Whenever Python knows what the result of a boolean expression is going to be, it stops evaluating. This is an optimization, but it also makes for some handy idioms, like assigning default values.

def do_a_thing(maybelist=None):
    # this is done all the time when you want the default argument to be
    # a list, but you don't want to make the mistake of a mutable default argument
    maybelist = maybelist or []

The implementation example you gave is confusing to someone who doesn't know about Euclid's Algorithm for calculating gcd's, because it's not obvious how the heck it actually calculates the gcd. I would say that that is an example of someone abusing short circuit evaluation.

Cody Piersall
  • 7,154
  • 2
  • 36
  • 52
1

In Python, the only integer that defaults to False is zero. Logical values perform short-circuiting, as one of the comments say.

With the statement:

a and b

If 'a' evaluates to False, then 'b' does not need to be evaluated, so the result of the expression is 'a', but if 'a' evaluates to True, then b still needs to be evaluated, so b will be the result of the expression unless a evaluates to False.

`or' works the other way around, which makes sense if you think about it.

Patrick Maupin
  • 7,331
  • 2
  • 21
  • 41
0

and and or are not strictly Boolean operators in Python. x and y and x or y will always evaluate to either x or y, depending on the "truthiness" of the value. False values are numerical zeros, empty strings, and empty containers; all other values are true. This means that

  • x and y == x if x is false; y is not evaluated
  • x and y == y if x is true
  • x or y == x if x is true; y is not evaluated
  • x or y == y if x is false

The only time either evaluates to an actual Boolean value is when the x or y value involved is an actual Boolean value.

chepner
  • 389,128
  • 51
  • 403
  • 529