Search code examples
pythonshort-circuiting

Python all short-circuiting with None element


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?


Solution

  • 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 in a 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.