9

I wish to use the Python all() function to help me compute something, but this something could take substantially longer if the all() does not evaluate as soon as it hits a False. I'm thinking it probably is short-circuit evaluated, but I just wanted to make sure. Also, is there a way to tell in Python how the function gets evaluated?

Dimitris Fasarakis Hilliard
  • 136,212
  • 29
  • 242
  • 233
Sylvester V Lowell
  • 1,065
  • 1
  • 9
  • 12

4 Answers4

18

Yes, it short-circuits:

>>> def test():
...     yield True
...     print('one')
...     yield False
...     print('two')
...     yield True
...     print('three')
...
>>> all(test())
one
False

From the docs:

Return True if all elements of the iterable are true (or if the iterable is empty). Equivalent to:

def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True

So when it returns False, then the function immediately breaks.

erip
  • 15,290
  • 10
  • 62
  • 113
TerryA
  • 56,204
  • 11
  • 116
  • 135
  • What does "yield" do? – Sylvester V Lowell Jun 22 '13 at 01:18
  • @SylvesterVLowell See [The Python yield keyword explained](http://stackoverflow.com/q/231767/395760) –  Jun 22 '13 at 01:19
  • @SylvesterVLowell Have a look [here](http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained). Simply, it's like a return statement, but you can return multiple values. But instead of returning a list, it returns a generator. – TerryA Jun 22 '13 at 01:19
  • In short, yield is how generators work. If you knew that `all` might short circuit, I guess you knew what generators *do*, just not how they work. – morningstar Jun 22 '13 at 01:22
  • The part that guarantees short circuiting is here: http://hg.python.org/cpython/rev/124237eb5de9 – jamylak Jun 22 '13 at 02:12
5

Yes, all does use short-circuit evaluation. For example:

all(1.0/x < 0.5  for x in [4, 8, 1, 0])
=> False

The above stops when x reaches 1 in the list, when the condition becomes false. If all weren't short-circuiting, we'd get a division by zero when x reached 0.

Óscar López
  • 225,348
  • 35
  • 301
  • 374
2

Make sure you don't do as I did initially which was to try to use short-circuiting to test for the existence of a method before calling it:

>>> class MyClass(object):
...    pass
...
>>> a=MyClass()
>>> all([hasattr(a,'b'), a.b()])

Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'b'

but

>>> a=MyClass()                   
>>> hasattr(a,'b') and a.b()   #doesn't evaluate a.b() as hasattr(a,'b') is false

False

In the first code snippet, Python evaluates the list before passing it to all() so it still throws the exception. This is basically the same as using list() to force all() not to use short-circuit evaluation as in morningstar's answer

Danny
  • 134
  • 3
  • 2
    On reflection, this fails because Python evaluates the list before passing it to all() so it is [hasattr(a,'b'), a.b()] that throws the exception rather than the all() statement itself. A line such as all([hasattr(a,'b') and a.b(),hasattr(c,'d') and c.d()]) would still work – Danny Jan 15 '15 at 11:11
  • Python would have to have REALLY lazy evaluation to do what you initially expected. – Blacklight Shining Apr 19 '15 at 19:18
  • You should edit that comment into your answer. – Blacklight Shining Apr 19 '15 at 19:23
0

In answer to your question of whether you can tell all to be either short-circuit evaluated or not, it is short-circuit by default, but if you wanted it not to be, you could do this:

result = all(list(iterable))

Though that has the possibly undesirable property that the whole list will be loaded into memory. I can't think how you would avoid that other than using a different function than all. For example

result = reduce(lambda x,y: x and y, iterable)
result = min(iterable) # surprisingly similar to all; YMMV if iterable contains non-booleans
morningstar
  • 8,561
  • 5
  • 27
  • 40
  • Don't use `min()` as a replacement for `all()` (or `max()` as a replacement for `any()`). `all()` will return `False` as soon as it encounters a falsey object; `min()` will always search the _entire_ iterable to see if there's a “lesser” one. `any()` and `max()` work similarly. Also, the four of them work on any iterable—don't waste time and space making a `list`. – Blacklight Shining Apr 19 '15 at 19:21