In case you assign to a variable name (that wasn't declared global
or nonlocal
) in a function or use the variable name in the argument list of the function the variable name will become part of the function.
In that case you could've used any variable name inside the function because it will always refer to the local variable that was passed in:
x = 50
def func(another_x):
print('local x =', another_x)
another_x = 2
print('local x =', another_x)
return another_x
print('global x =', x)
x = func(x) # note that I assigned the returned value to "x" to make the change visible outside
print('global x =', x)
More explanation
I'm going to try to show what I meant earlier when I said that x
with "will become part of the function".
The __code__.co_varnames
of a function holds the list of local variable names of the function. So let's see what happens in a few cases:
If it's part of the signature:
def func(x): # the name "x" is part of the argument list
pass
print(func.__code__.co_varnames)
# ('x',)
If you assign to the variable (anywhere in the function):
def func():
x = 2 # assignment to name "x" inside the function
print(func.__code__.co_varnames)
# ('x',)
If you only access the variable name:
def func():
print(x)
print(func.__code__.co_varnames)
# ()
In this case it will actually look up the variable name in the outer scopes because the variable name x
isn't part of the functions varnames!
I can understand how this could confuse you because just by adding a x=<whatever>
anywhere in the function will make it part of the function:
def func():
print(x) # access
x = 2 # assignment
print(func.__code__.co_varnames)
# ('x',)
In that case it won't look up the variable x
from the outer scope because it's part of the function now and you'll get a tell-tale Exception (because even though x
is part of the function it isn't assigned to yet):
>>> func()
UnboundLocalError: local variable 'x' referenced before assignment
Obviously, it would work if you access it after assigning to it:
def func():
x = 2 # assignment
print(x) # access
Or if you pass it in when you call the function:
def func(x): # x will be passed in
print(x) # access
Another important point about local variable names is that you can't set variables from outer scopes, except when you tell Python to explicitly not make x
part of the local variable names, for example with global
or nonlocal
:
def func():
global x # or "nonlocal x"
print(x)
x = 2
print(func.__code__.co_varnames)
# ()
This will actually overwrite what the global (or nonlocal) variable name x
refers to when you call func()
!