How is `x = 42; x = lambda: x` parsed?

Question:

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
Asked By: max

||

Answers:

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'
Answered By: Karl Knechtel

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.

Answered By: tdelaney

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)
Answered By: molbdnilo
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.