86

For example, if I have the following statement:

if( foo1 or foo2)
    ...
    ...

if foo1 is true, will python check the condition of foo2?

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
ogama8
  • 999
  • 1
  • 6
  • 6
  • See http://docs.python.org/2/reference/expressions.html#boolean-operations. – Martijn Pieters Dec 19 '12 at 20:32
  • 11
    My usual tip for keywords, if -- like me -- you're too lazy to load the official docs: type `help("or")` at the interpreter console. In this case, read the fourth paragraph. – DSM Dec 19 '12 at 20:33
  • 3
    Python's behavior here has nothing to do with "`if`" and everything to do with "`or`." – jwodder Oct 19 '13 at 21:40
  • There are [some modules that implement lazy evaluation in Python](http://stackoverflow.com/questions/5295038/python-lazy-evaluator), which may be what you are looking for. – Anderson Green Oct 20 '13 at 18:49
  • Technically, python short-circuits and then *double-evaluates* the result of a boolean operator, if it is later used an actual boolean... unless it is directly in an `if` statement ... which is privileged (more than not or bool()), and so it evaluates them once. The double-evaluation depends on the complexity of the operation. This is counter intuitive, but there is proof here: https://gist.github.com/earonesty/08e9cbe083a5e0583feb8a34cc538010 – Erik Aronesty Oct 16 '19 at 16:45

7 Answers7

113

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.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • 5
    I would point out that it's also possible to make bitwise comparisons, which aren't lazy, like [Eric Wang's answer](https://stackoverflow.com/a/37342386/6381989) below. You can also check out the [documentation](https://docs.python.org/2/reference/expressions.html#binary-bitwise-operations). – Bruno Silvano May 25 '18 at 16:57
71

and or is lazy

& | is not lazy

Eric Wang
  • 1,009
  • 1
  • 9
  • 16
25

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.

Erik Veloso
  • 359
  • 3
  • 2
7

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

Pete Cacioppi
  • 880
  • 7
  • 11
  • 2
    Lazy evaluation is sort of a catch-all term that can refer to short-circuiting behaviour of logical operators as well. If you mean call-by-need it is usually called "call-by-need" since lazy evaluation can mean so much. – kqr Oct 19 '13 at 22:15
  • 1
    I disagree. People who say 'lazy evaluation' when they mean 'short circuit evaluation' are just wrong. 'call by need' is a synonym for 'lazy evaluation'. I agree it's less prone to misuse. – Pete Cacioppi May 07 '15 at 20:07
  • 2
    @PeteIsNeat if you want to be a technical prescriptivist, "lazy evaluation" is one of many ways of *implementing* call-by-need semantics. They are not synonyms and not equivalent. I'm a descriptivist so when people talk about "lazy behaviour" and it's obvious from the context they are talking about short-circuiting I don't really mind. :) – kqr May 07 '15 at 20:22
  • 1
    I was the one introducing "lazy evaluation" to the conversation. I was only trying add some added context. It's misleading to say "lazy evaluation" if you mean "short circuit evaluation", and I wanted to make sure everyone understood that. As to whether "lazy evaluation" is synonymous with "call by need" ... the wikipedia [page](http://en.wikipedia.org/wiki/Lazy_evaluation) looks correct to me. – Pete Cacioppi May 07 '15 at 20:36
  • [Wikipedia contradicts itself in this case](http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_need): "Lazy evaluation is the most commonly used implementation strategy for call-by-need semantics, but variations exist — for instance optimistic evaluation." OP sorta-kinda introduced lazy evaluation to the conversation by asking how to "evaluate things lazily". – kqr May 08 '15 at 08:19
  • This debate is getting silly. There is a distinction between "lazy evaluation" and "call by need" only in very academic and obscure situations (non deterministic programming). I'm just not seeing any disconnect between my basic points regarding the terms "lazy evaluation", "short circuit evaluation", and "call by need". The first and last are synonyms (for all practical purposes) whereas short circuit evaluation means something else. – Pete Cacioppi May 08 '15 at 21:30
  • This is not actually caused by short circuit evaluation. Consider this custom `or` function: `def myor(a, b): return a or b`. This function still short-circuits, since `or` short-circuits, but suppose we have `x = None`. Then with the regular `or`, this succeeds: `x is None or x < 3`, but with our function version, `myor(x is None, x < 3)`, we get an error saying we can't order `NoneType` and `int`. Both versions use short-circuit evaluation, but only one of them is lazy. – zstewart Sep 16 '15 at 05:41
  • I think its most accurate to say that short-circuit evaluation is a special form of lazy evaluation applied only to built in boolean functions. Python is, in general, not a lazy evaluation language. But it can be said to support lazy evaluation in a limited way, in as much as it supports short circuit evaluation. – Pete Cacioppi Sep 22 '15 at 20:24
  • The thing is, short circuit boolean evaluation is very common among different languages, whereas general purpose lazy evaluation is very rare. So its most clear to say Python is not a lazy evaluation language, but it does provide short circuit boolean expressions. – Pete Cacioppi Sep 22 '15 at 20:30
3

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.

J.J
  • 3,459
  • 1
  • 29
  • 35
2

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.

jdotjdot
  • 16,134
  • 13
  • 66
  • 118
  • 1
    For that use case, I think `if mydict.get("key") == 'heyyo!':` would work too -- `.get` will return `None` (or a specified default) if the key isn't found. – DSM Dec 19 '12 at 20:34
  • Correct, it will, so I don't use it generally for actual dictionaries, but for dictionary-like objects (usually from external modules) that don't have a `.get` method. – jdotjdot Dec 19 '12 at 20:35
  • @DSM Actually for your use case, I would simply do `if mydict.get("key"):`, since `None` would evaluate to `False`. – jdotjdot Dec 19 '12 at 21:02
  • ? But then you'd lose the `'heyyo!'` comparison. – DSM Dec 19 '12 at 21:05
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
dansalmo
  • 11,506
  • 5
  • 58
  • 53