In the Execution Model section of the Python 3.7 reference manual I read the following statement:
The
global
statement has the same scope as a name binding operation in the same block. If the nearest enclosing scope for a free variable contains aglobal
statement, the free variable is treated as a global.
So I typed the following code into the Python Interpreter:
x =0
def func1():
global x
def func2():
x = 1
func2()
After calling func1()
I would have expected the value of x
in the global scope to change to 1
.
What did I get wrong?
x = 1
in func2
is not a free variable. It's just another local; you are binding to the name and names bound to are, by default, locals unless you tell Python otherwise.
From the same Execution model documentation:
If a name is bound in a block, it is a local variable of that block, unless declared as
nonlocal
orglobal
. [...] If a variable is used in a code block but not defined there, it is a free variable.
(Bold emphasis mine)
You bound the name in the block with x = 1
, so it is a local variable in that block, and can't be a free variable. So section you found doesn't apply, because that would only apply to free variables:
If the nearest enclosing scope for a free variable contains a
global
statement, the free variable is treated as a global.
You should not bind x
in func2()
, because only names that are not binding in the scope are free variables.
So this works:
>>> def func1():
... global x
... x = 1
... def func2():
... print(x) # x is a free variable here
... func2()
...
>>> func1()
1
>>> x
1
x
in func2
is now a free variable; it is not defined in the scope of func2
, so picks it up from the parent scope. The parent scope here is func1
, but x
is marked a global there so when reading x
for the print()
function the global value is used.
Contrast this with x
not being marked as a global in func1
:
>>> def func1():
... x = 1
... def func2():
... print(x) # x is free variable here, now referring to x in func1
... func2()
...
>>> x = 42
>>> func1()
1
Here the global name x
is set to 42
, but this doesn't affect what is printed. x
in func2
is a free variable, but the parent scope func1
only has x
as a local name.
It becomes all the more interesting when you add a new outer-most scope where x
is still local:
>>> def outerfunc():
... x = 0 # x is a local
... def func1():
... global x # x is global in this scope and onwards
... def func2():
... print('func2:', x) # x is a free variable
... func2()
... print('outerfunc:', x)
... func1()
...
>>> x = 42
>>> outerfunc()
outerfunc: 0
func2: 42
>>> x = 81
>>> outerfunc()
outerfunc: 0
func2: 81
x
in outerfunc
is bound, so not a free variable. It is therefore a local in that scope. However, in func1
, the global x
declaration marks x
as a global in the nested scrope. In func2
x
is a free variable, and by the statement that you found, it is treated as a global.