Calling locals() in a function not intuitive?

This may be elementary, but may help me understand namespaces.
A good explanation might step through what happens when the
function definition is executed, and then what happens later
when the function object is executed.
Recursion may be complicating things.

The results aren’t obvious to me; I would have expected:

locals_1 would contain var;
locals_2 would contain var and locals_1; and
locals_3 would contain var, locals_1, and locals_2

# A function calls locals() several times, and returns them ...
def func():
  var = 'var!'
  locals_1 = locals()
  locals_2 = locals()
  locals_3 = locals()
  return locals_1, locals_2, locals_3

# func is called ...
locals_1, locals_2, locals_3 = func()

# display results ...
print 'locals_1:', locals_1
print 'locals_2:', locals_2
print 'locals_3:', locals_3

Here are the results:

locals_1: {'var': 'var!', 'locals_1': {...}, 'locals_2': {...}}
locals_2: {'var': 'var!', 'locals_1': {...}, 'locals_2': {...}}
locals_3: {'var': 'var!', 'locals_1': {...}, 'locals_2': {...}}

The pattern seems to be, with (n) calls to locals, all of
the returned locals-dicts are identical, and they all include
the first (n-1) locals-dicts.

Can someone explain this?

More specifically:

Why does locals_1 include itself?

Why does locals_1 include locals_2? Is locals_1 assigned when
func is created, or executed?

And why is locals_3 not included anywhere?

Does “{…}” indicate an ‘endless recursion’? Sort of like
those photos of mirrors facing each other?

Best answer

Let’s run this code:

def func():
  var = 'var!'
  locals_1 = locals()
  print(id(locals_1), id(locals()), locals())
  locals_2 = locals()
  print(id(locals_2), id(locals()), locals())
  locals_3 = locals()
  print(id(locals_3), id(locals()), locals())
  return locals_1, locals_2, locals_3


func()

This would be in the output:

44860744 44860744 {'locals_1': {...}, 'var': 'var!'}
44860744 44860744 {'locals_2': {...}, 'locals_1': {...}, 'var': 'var!'}
44860744 44860744 {'locals_2': {...}, 'locals_3': {...}, 'locals_1': {...}, 'var': 'var!'}

locals() here grows as expected, however you are assigning the reference to locals(), not the value of locals() to each variable.

After each assignment locals() gets changed, but reference does not, so each variable is pointing to the same object. In my output all object ids are equal, this is the proof.

Longer explanation

These variables has the same link (reference) to that object. Basically, all variables in Python are references (similar concept to pointers).

        locals_1            locals_2                 locals_3
            \                    |                      /
             \                   |                     /
              V                  V                    V
            ---------------------------------------------
            |            single locals() object         |
            ---------------------------------------------

They have absolutely no idea what value does locals() have, they only know where to get it when it’s needed (when the variable is used somewhere). Changes on locals() don’t affect those variables.

In the end of you function you’re returning three variables, and this is what happening when you’re printing them:

print(locals_N) -> 1. Get object referenced in locals_N
                   2. Return the value of that object

See? So, this is why they have exactly the same value, the one locals() has at the moment of print.

If you change locals() (somehow) again and then run print statements, what would be printed 3 times? Yep, the NEW value of locals().