-2

I'm confused about python's handing of objects during execution of all. The documentation says that it is equivalent to:

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

From this trivial example, it seems like that is not the case:

class Foo:
  def __init__(self, v):
    self.v = v

a = Foo(5)
b = None

if a and b and a.v == b.v:
  print("hello1")
else:
  print("goodbye1")

if all([a, b, a.v == b.v]):
  print("hello2")
else:
  print("goodbye2")

This causes the all line to crash with the error:

goodbye1

AttributeError: 'NoneType' object has no attribute 'v' Line 13 in (Solution.py)

I looks like the cpython implementation should prevent this failure, but it doesn't. What's going on here? Why does the same expression crash within all, but works as expected when expanded?

Thierry Lathuille
  • 23,663
  • 10
  • 44
  • 50
fvrghl
  • 3,642
  • 5
  • 28
  • 36
  • I thought this was a well researched question; can someone explain why this might have been downvoted? I'd like to improve my questions going forward. – fvrghl Jan 11 '20 at 23:12
  • Does this answer your question? [Why do I get AttributeError: 'NoneType' object has no attribute 'something'?](https://stackoverflow.com/questions/8949252/why-do-i-get-attributeerror-nonetype-object-has-no-attribute-something) – AMC Jan 12 '20 at 03:19
  • @AMC: No, it does not. I have rephrased the question so that it's clear that is issue is around the order of evaluation of expressions. – fvrghl Jan 12 '20 at 06:30
  • Please don't change the question like you just did: it is now a completely different one, which makes all related answers more or less irrelevant. You should revert it to its original version and ask the edited version as a new, different question. – Thierry Lathuille Jan 12 '20 at 07:10

4 Answers4

3

Your list [a, b, a.v == b.v] has to be created before it can be passed to all, and all of its items have to be created before that, so a.v == b.v will have to be evaluated, and will fail as b is None.

On the other hand, if a and b and a.v == b.v: doesn't need to try and evaluate a.v == b.v, as Python knows that the whole expression will be False once it encounters b, which is falsy.

There is nothing wrong with the equivalent given in the documentation: iterable would have to be created first, but your list just can't be created.

Thierry Lathuille
  • 23,663
  • 10
  • 44
  • 50
0

Your error is happening because before you call all you attempt to construct a list containing a.v == b.v

donkopotamus
  • 22,114
  • 2
  • 48
  • 60
0

It's because of lazy evaluation of boolean expression. It stops on 'and b' because b is None and not even try to get b.v.

  • 1
    Lazy evaluation isn't happening here, that is short-circuiting which `all` does as well, but the list is already created before `all` is involved – juanpa.arrivillaga Jan 11 '20 at 22:53
0

The first if statement uses short-circuiting and stops when it hits b. Since b = None this line evaluates to False and further evaluations are stopped early.

if a and b and a.v == b.v:
          ^--- Evaluation stops here.

This is why you don't see an error for that line, even you though you have b.v which normally would trigger an attribute error.

Using all evaluates all of the elements. Since b is None, you will see the attribute error.

if all([a, b, a.v == b.v]):
                       ^---AttributeError here, None does not have a .b attr
James
  • 32,991
  • 4
  • 47
  • 70