Does Python evaluate if's conditions lazily?
Question:
For example, if I have the following statement:
if( foo1 or foo2)
...
...
if foo1 is true, will python check the condition of foo2?
Answers:
Yes, Python evaluates boolean conditions lazily.
The docs say,
The expression x and y first evaluates x; if x is false, its value is
returned; otherwise, y is evaluated and the resulting value is
returned.
The expression x or y first evaluates x; if x is true, its value is
returned; otherwise, y is evaluated and the resulting value is
returned.
Yes, Python evaluates lazily, so foo2
will not be checked.
I use this all the time for grabbing items from dictionary-like objects if I don’t know if the key exists:
if 'key' in mydict and mydict['key'] == 'heyyo!':
do_stuff()
See @unutbu’s answer for a fuller explanation.
This isn’t technically lazy evaluation, it’s short-circuit boolean expressions.
Lazy evaluation has a somewhat different connotation. For example, true lazy evaluation would likely allow this
def foo(arg) :
print "Couldn't care less"
foo([][0])
But Python doesn’t.
Python is also nice in that it “echos” it’s boolean arguments. For example, an or condition returns either it’s first “truthy” argument or the last argument (if all arguments are “falsey”). An and condition does the inverse.
So “echo argument” booleans means
2 and [] and 1
evaluates to [], and
[] or 1 or 2
evaluates to 1
It is really the or
part that is short circuited:
>>> 1 or 1/0 #also 0 and 1/0
1
>>> 0 or 1/0 #also 1 and 1/0
Traceback (most recent call last):
File "<pyshell#1240>", line 1, in <module>
0 or 1/0
ZeroDivisionError: integer division or modulo by zero
Python’s laziness can be proved by the following code:
def foo():
print('foo')
return False
def bar():
print('bar')
return False
foo() and bar() #Only 'foo' is printed
On the other hand,
foo() or bar()
would cause both ‘foo’ and ‘bar’ to be printed.
and
or
is lazy
&
|
is not lazy
A short demo would be to compare the time difference between
all(xrange(1,1000000000))
and
any(xrange(1,1000000000))
The all() has to check every single value, whilst the any() can give up after the first True has been found. The xrange, being a generator, therefore also gives up generating things as soon as the evaluator is done. For this reason, the all will consume large amounts of RAM and take ages, whilst the any will use just a few bytes and return instantaneously.
For example, if I have the following statement:
if( foo1 or foo2)
...
...
if foo1 is true, will python check the condition of foo2?
Yes, Python evaluates boolean conditions lazily.
The docs say,
The expression x and y first evaluates x; if x is false, its value is
returned; otherwise, y is evaluated and the resulting value is
returned.The expression x or y first evaluates x; if x is true, its value is
returned; otherwise, y is evaluated and the resulting value is
returned.
Yes, Python evaluates lazily, so foo2
will not be checked.
I use this all the time for grabbing items from dictionary-like objects if I don’t know if the key exists:
if 'key' in mydict and mydict['key'] == 'heyyo!':
do_stuff()
See @unutbu’s answer for a fuller explanation.
This isn’t technically lazy evaluation, it’s short-circuit boolean expressions.
Lazy evaluation has a somewhat different connotation. For example, true lazy evaluation would likely allow this
def foo(arg) :
print "Couldn't care less"
foo([][0])
But Python doesn’t.
Python is also nice in that it “echos” it’s boolean arguments. For example, an or condition returns either it’s first “truthy” argument or the last argument (if all arguments are “falsey”). An and condition does the inverse.
So “echo argument” booleans means
2 and [] and 1
evaluates to [], and
[] or 1 or 2
evaluates to 1
It is really the or
part that is short circuited:
>>> 1 or 1/0 #also 0 and 1/0
1
>>> 0 or 1/0 #also 1 and 1/0
Traceback (most recent call last):
File "<pyshell#1240>", line 1, in <module>
0 or 1/0
ZeroDivisionError: integer division or modulo by zero
Python’s laziness can be proved by the following code:
def foo():
print('foo')
return False
def bar():
print('bar')
return False
foo() and bar() #Only 'foo' is printed
On the other hand,
foo() or bar()
would cause both ‘foo’ and ‘bar’ to be printed.
and
or
is lazy
&
|
is not lazy
A short demo would be to compare the time difference between
all(xrange(1,1000000000))
and
any(xrange(1,1000000000))
The all() has to check every single value, whilst the any() can give up after the first True has been found. The xrange, being a generator, therefore also gives up generating things as soon as the evaluator is done. For this reason, the all will consume large amounts of RAM and take ages, whilst the any will use just a few bytes and return instantaneously.