Schrödinger's variable

▶ Schrödinger's variable *

The asterisk at the end of the title indicates the example was not present in the first release and has been recently added.

def make_func():
    for target in ('a', 2, None):
        if isinstance(target, str):
            def f():
                print(f'inside f(), {target=}')
            print(f'just created f(), {target=} at this point!')
    return f

Output (Python version):

>>> f = make_func()
target='a' at this point!

>>> f()
inside f(), target=None

💡 Explanation:

  • Python doesn't actually bind the value of target in f(). It just creates a function that will look up target in the surrounding context.

  • Since target will continue to be updated until it is set to None in the last iteration of the for loop, target=None when it prints inside of f()

    Output:

>>> f.__code__.co_freevars
('target',)
>>> import inspect
>>> print(inspect.getclosurevars(f).nonlocals['target'])
None

This might be made a bit clearer if we use a global variable rather than a free variable:

for target in ('a', 2, None):
    if isinstance(target, str):
        def f():
            print(f'inside f(), {target=}')
        print(f'just created f(), {target=} at this point!')

Output.

just created f(), target='a' at this point!
>>> print(target)
None
>>> f()
inside f(), target=None
>>> target = "whoa"
inside f(), target='whoa'