How is `x = 42; x = lambda: x` parsed?
I was surprised that this assertion fails:
x = 42
x = lambda: x
assert x() == 42
It seems that x
ends up recursively referring to itself, so that x()
, x()()
, etc. are all functions.
What is the rule used to parse this, and where is this documented?
By the way (not unexpectedly given the above), the original value of x
has no references left after the lambda definition:
class X:
def __del__(self): print('deleting')
x = X()
x = lambda: x # 'deleting' is printed here
Solution 1:
The variable x
is created by the first assignment, and rebound with the second assignment.
Since the x
in the lambda isn't evaluated until the lambda is called, calling it will evaluate to the most recently assigned value.
Note that this is not dynamic scoping - if it were dynamic, the following would print "99", but it prints "<function ...":
x = 42
x = lambda: x
def test(f):
x = 99
print(f())
test(x)
Solution 2:
The first assignment is irrelevant; the x
in the body of the lambda
is bound late:
x = lambda: x # no need for a prior assignment
x = lambda: y # notice: no NameError occurs, *until it is called*
This is the same reason that creating lambdas in a loop is tricky, and is also used to make trees with the standard library defaultdict
:
tree = lambda: defaultdict(tree)
t = tree()
t['foo']['bar']['baz'] = 'look ma, no intermediate steps'
Solution 3:
A lambda is an anonymous function object. Python completely resolves whatever is on the right side of an equation to a single anonymous object and then resolves whatever is on the left side for assignment.
x = lambda: x
first compiles lambda: x
into a function object that returns whatever happens to be in x
at the time it is called. It then rebinds x
with this function object, deleting whatever object happened to be there before.
Now x
is a function that returns whatever is in x
... which is a function that returns whatever is in x
, etc... So you can write x()()()()()()
as many times as you want, and still get that orginal lambda:x
function object.
Python functions have a local namespace but only variables assigned in the function reside there. Since x
isn't assigned in the lambda
, it's resolved in the containing scope - that is, the module level "x". An identical piece of code is
def x():
return x
Contrast this with
def x():
x = 1
return x
Now, the parameter x
is a local variable and is unrelated to the global x
.