I read everywhere that Python all
and any
functions support short-circuiting.
However:
a = None
all((a is not None, a + 1 > 2))
Throws the following error:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/IPython/core/interactiveshell.py", line 3331, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-4-6e28870e65c8>", line 1, in <module>
all((a is not None, a + 1 > 2))
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
I would have expected the code not to evaluate a + 1 > 2
since a
is None
.
Why is this happening? Is this because each term is evaluated before the call? Am I forced to use the and
operator as in a is not None and a + 1 > 2
?
The tuple (a is not None, a + 1 > 2)
needs to be created before all()
can be called. It is during the creation of the tuple that the TypeError
is raised. all()
doesn't even get a chance to run.
If you want to see all
's short circuiting in action, pass it a generator expression. For example:
>>> all('foobaR'[i].islower() for i in range(7))
False
>>> all('foobar'[i].islower() for i in range(7))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
IndexError: string index out of range
In the first run all
stops when it hits the 'R'.islower()
case since that's False
. In the second run it keeps going until i == 6
, which triggers an index error.
Am I forced to use the
and
operator as ina is not None and a + 1 > 2
?
I wouldn't say "forced"—I'm sure there other convoluted options—but yes, that's the obvious and idiomatic way to write it.