pythonmemory-managementscopenamespacespython-nonlocal

What does exactly "nonlocal" keyword do with a variable?


Here is an example code that I have made to try to understand the mechanics of "nonlocal" keyword. `

# Outer fuction
def func1():
  var1 = 2
  print("---ID of var1 in func1---")
  print(id(var1))
  print(locals())
# Inner function
  def func2():
    nonlocal var1
    var1 += 1
    print("---ID of var1 in func2---")
    print(id(var1))
    print(locals())

  # Call inner function
  func2()
  print("---ID of var1 in func1 after func2 execution---")
  print(id(var1))
  print(locals())

func1()

` We define var1 in func1() namespace. After calling 'nonlocal' in func2(), var1 changes its id and start to appear in local dictionary of func2(). After execution of func2(), it is still exists in local dictionary of func1().

Does it mean that a variable can exist in several namespaces in the same time? Why did id(var1) changed?

I expected that var1 will be only in local dictionary of func1() and 'nonlocal' keyword will just give a possibility to change it in func2().


Solution

  • locals() is a very weird function that doesn't do what anyone would reasonably expect it to, with some bizarre undocumented quirks. This particular quirk happens to be documented, but it's still really weird. Generally, you shouldn't use locals() unless you really have no other choice.

    The dict returned by locals() is

    When called inside a function, the dict returned by locals() will include nonlocal variables:

    Free variables are returned by locals() when it is called in function blocks, but not in class blocks.

    That doesn't mean the variable actually exists in multiple namespaces at the same time. It means locals() is weird.


    As for the ID change, IDs are associated with objects, not variables. The integer object var1 refers to after var1 += 1 is a different object with a different ID than the previous integer object.