How do global and local variables behave in this case?

828 views Asked by At

This is saved in my file function_local.py:

x = 50

def func(x):
    print('x is', x)
    x = 2
    print('Changed local x to', x)

func(x)
print('x is still', x)

Output:

$ python function_local.py
x is 50
Changed local x to 2
x is still 50

Question: When we print the first line inside the Function, why does it print out 50, not 2? Even if it is said, in the function, that x = 2?

3

There are 3 answers

0
Gaurang Shah On

this could be easily understand by this.

x = 50

def func(x):
    print "Local variables: "+str(locals())
    print('x is', x)
    x = 2
    print "Local variables: "+str(locals())
    print('Changed local x to', x)

func(x)
print('x is still', x)

and the output

Local variables: {'x': 50}
'x is', 50
Local variables: {'x': 2}
'Changed local x to', 2
'x is still', 50

however if you want to access the x from outer scope after defining the x in func there is a way, I am not sure if it would create a issue or not though.

def func(x):
    x = 2
    print "local x == %s" % x
    print "global x == %s" % globals().get('x')

func(x)
print "x is still %s" % x 

Following will be output of the run.

local x == 2
global x == 50
x is still 50
0
MSeifert On

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()!

2
chepner On

In your function

def func(x):
 print('x is', x)
 x = 2
 print('Changed local x to', x)

x is not a global variable; it is a local variable due to the assignment on the second line of the body. If you are going to assign to a global variable, it must be explicitly declared as such:

def func(x):
 global x
 print('x is', x)
 x = 2
 print('Changed local x to', x)

(Note: I missed that x is actually a function parameter. See https://nedbatchelder.com/text/names.html for an explanation of assignments to local names do not affect global names.)