Search code examples
pythoncpython

Differences between forced early binding methods (In CPython) and how to access values stored in function?


Using the CPython implementation of Python 3.9.16 consider the following codes

def foo_a():
    x = 1
    fun = (lambda _x: lambda: print(_x))(x)
    x = 2
    return fun

def foo_b():
    x = 1
    fun = lambda _x=x: print(_x)
    x = 2
    return fun

fa = foo_a()
fb = foo_b()

where I've used two different methods for forcing early binding of x. I believe these are semantically equivalent, however I notice that the resulting functions have different closures.

print(fa.__closure__)    # (<cell at 0x0000025CED84BFD0: int object at 0x0000025CE9E36930>,)
print(fb.__closure__)    # None

In the case of fa I can access the local _x variable using fa.__closure__[0].cell_contents, but where is the local _x stored in fb if not inside its closure?


Solution

  • In your first example, _x is a free variable whose value is found in a non-local scope carried by the inner function via a closure. (Informally, we say the inner function closes over the variable _x in the outer function.)

    In your second example, _x is a local variable assigned at call time by an argument to fun, or if none is provided, from a default value stored in the function itself.

    Your use of both functions is the same, but if you received a reference to the inner function in the first example, it would be "harder" to change the value of _x (you'd have to poke around in the closure itself; closures are, for better or worse, mutable) than it would be to change the value of _x in the second example (you would just pass a different argument).