4

Based on what I've seen on other stackoverflow pages:

the following code should short circuit:

any(True, 2+2, False, 2/0)
all(True, 2+2, False, 2/0)
any([True, 2+2, False, 2/0])
all([True, 2+2, False, 2/0])

but for every one of them I get a ZeroDivisionError: division by zero.

Am I missing something? Why does it error?

wjandrea
  • 23,210
  • 7
  • 49
  • 68
hainabaraka
  • 363
  • 1
  • 3
  • 9

2 Answers2

4

Your code errors because the expressions have to be evaluated before being passed into the function.

In this context, short-circuiting actually means that as soon as they find a different value, they return and don't bother checking the remaining values. It's not the same kind of short-circuiting that and and or do, which can actually avoid evaluating expressions.

To illustrate, let's use an iterator, which will get consumed:

>>> a = iter([1, 0, 2, 3])
>>> all(a)  # Search until a falsy value
False
>>> list(a)  # Show what's left in the iterator
[2, 3]

You can see that it consumed 1 and 0, returning on 0 because it's falsy.


Now, if you did want to do lazy evaluation with any and all, you could call lambdas in a generator expression:

>>> any(e() for e in [lambda: 1, lambda: 0, lambda: 2/0])
True
>>> all(e() for e in [lambda: 1, lambda: 0, lambda: 2/0])
False

(Thanks to this question for inspiring this bit.)

wjandrea
  • 23,210
  • 7
  • 49
  • 68
1

Yes, short circuting happens in python

In [23]: False and 3/0
Out[23]: False

In [24]: True and 3/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-24-a08823d6496a> in <module>
----> 1 True and 3/0

ZeroDivisionError: division by zero

Using the dis module,

In [32]: dis.dis("all([True, 2+2, False, 2/0])")
  1           0 LOAD_NAME                0 (all)
              2 LOAD_CONST               0 (True)
              4 LOAD_CONST               1 (4)
              6 LOAD_CONST               2 (False)
              8 LOAD_CONST               3 (2)
             10 LOAD_CONST               4 (0)
             12 BINARY_TRUE_DIVIDE
             14 BUILD_LIST               4
             16 CALL_FUNCTION            1
             18 RETURN_VALUE

In [33]: dis.dis("any(True, 2+2, False, 2/0)")
  1           0 LOAD_NAME                0 (any)
              2 LOAD_CONST               0 (True)
              4 LOAD_CONST               1 (4)
              6 LOAD_CONST               2 (False)
              8 LOAD_CONST               3 (2)
             10 LOAD_CONST               4 (0)
             12 BINARY_TRUE_DIVIDE
             14 CALL_FUNCTION            4
             16 RETURN_VALUE

When you look at the function calls, BINARY_TRUE_DIVIDE is evaluated before any or all because expressions get evaluated first before any function call

bigbounty
  • 14,834
  • 4
  • 27
  • 58