[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