[Tutor] Use of "or" in a lambda expression

Steven D'Aprano steve at pearwood.info
Sat Apr 4 19:34:11 CEST 2015


On Sat, Apr 04, 2015 at 11:49:08AM -0500, boB Stepp wrote:
> Windows 7, Python 3.4.3
> 
> This code snippet is "Example 7-13" on page 383 from "Programming
> Python, 4th ed." by Mark Lutz :
> 
> import sys
> from tkinter import *
> 
> widget = Button(None,
>             text='Hello event world!',
>             command=(lambda: print('Hello lambda world!') or sys.exit()))

o_O

That's either the most horrible misuse of lambda I've ever seen, or a 
really cool and rather nifty trick. I'm not sure which :-)


> widget.pack()
> widget.mainloop()
> 
> My question is about the lambda expression. The author states "...this
> version uses an or operator to force two expressions to be run..."  I
> am not understanding how 'or' causes this to happen. I guess I am
> expecting the 'or' to result only in the print running without
> executing sys.exit(). But that is not what happens--of course. I tried
> substituting 'and' for 'or', but this results in only the print being
> run! Obviously I have a significant misunderstanding of what is going
> on.


Both `or` and `and` are "short-circuit" operators. Here is a truth-table 
for `a or b`:

           |a = True   False
-----------+-----------------
b = True   |    True   True
    False  |    True   False


If `a` is true, then `a or b` is true, regardless of whether b is true 
or false. So we can say that if `a` is true, `a or b` returns `a`.

If `a` is false, then `a or b` returns true if, and only if, `b` is 
true; otherwise it returns false. So we can say that if `a` is false, 
then `a or b` returns `b`.

So we can define our own "or" function like this:

def or_ (a, b):
    if a: return a
    else: return b

Unlike this function version, the `or` operator doesn't evaluate the `b` 
expression unless needed. So this piece of code:

    print(msg) or sys.exit()

runs like this:

(1) Evaluate the expression on the left of the operator: print(msg).
(2) That has the side-effect of printing the message, and returns
    the value None.
(3) Is None a true value? If so, `or` can short-circuit, and return
    it as its result.
(4) But None is a false value, so evaluate the expression on the
    right of the operator: sys.exit()
(5) Which has the side-effect of exiting the interpreter.
(6) The `or` operator would now return the result of sys.exit(), 
    if it had one; but it doesn't, since the interpreter has just 
    exited.


A cleaner example might be this:


py> def true():
...     print("calling true")
...     return 23  # a true-ish value
...
py> def false():
...     print("calling false")
...     return 0  # a false-ish value
...
py> true() or false()  # only the left operand is evaluated
calling true
23
py> true() or true()
calling true
23
py> false() or true()  # both operands are evaluated
calling false
calling true
23
py> false() or false()
calling false
calling false
0


The `and` operator is similar, except the truth-table looks like this:


           |a = True   False
-----------+-----------------
b = True   |    True   False
    False  |    False  False


Again, the right-hand operand is only evaluated if it is needed. This 
lets us write code like this:

    if mylist and mylist[0] == spam: eggs()


If mylist is empty, then `mylist and ...` doesn't even need to evaluate 
the right-hand operand, mylist[0] doesn't run and so does not fail. Only 
if mylist is a truthy value does mylist[0] run.


-- 
Steve


More information about the Tutor mailing list