I have a short example python script that I'm calling glbltest.py:
a = []
def fun():
global a
a = [20,30,40]
print("before ",a)
fun()
print("after ",a)
If I run it from the command line, I get what I expect:
$ python glbltest.py
before []
after [20, 30, 40]
I open a python shell and run it by importing, and I get basically the same thing:
>>> from glbltest import *
before []
after [20, 30, 4]
So far so good. Now I comment out those last three lines and do everything "by hand":
>>> from glbltest import *
>>> a
[]
>>> fun() # I run fun() myself
>>> a # I look at a again. Surely I will get the same result as before!
[] # No! I don't!
What is the difference between fun()
being run "automatically" by the importing of the script, and me running fun()
"by hand"?
global a
refers to the name a
in the glbltest
module's namespace. When you set a
by hand, it refers to the name a
in the __main__
module's namespace.
When you use from glbltest import *
the names in the module are imported into the __main__
module's namespace. Those are different names but refer to the same objects. When you use global a
and a = [20,30,40]
in the glbltest
module, assignment makes a new object that a
in glbltest
module's namespace now refers to. The name a
in the __main__
module still refers to the original object (the empty list).
As a simple example, print the id()
of a
in the fun()
function, and print(id(a))
"by hand" after you set it:
a = []
def fun():
global a
print(a, id(a))
a = [20,30,40]
print(a, id(a))
# To view the global a object id again
def show():
print(a, id(a))
"by hand", with comments:
>>> from glbltest import *
>>> a # the imported name
[]
>>> id(a) # its object ID
2056911113280
>>> fun()
[] 2056911113280 # start of fun() the object is the same ID
[20, 30, 40] 2056902829312 # but assignment changes to new object (different ID)
>>> a
[] # main a still refers to original object
>>> id(a)
2056911113280
>>> show() # glbltest module still sees *its* global a
[20, 30, 40] 2056902829312
Note that if you use mutation vs. assignment to change the existing object. You'll see the change:
a = []
def fun():
global a
print(a, id(a))
a.extend([20,30,40]) # modify existing object, not assigning a new object.
print(a, id(a))
# To view the global a object id again
def show():
print(a, id(a))
Now the object IDs remain the same.
>>> from glbltest import *
>>> a, id(a) # import object
([], 1408887112064)
>>> fun()
[] 1408887112064 # before change still the same object
[20, 30, 40] 1408887112064 # mutated the *existing* list
>>> a, id(a)
([20, 30, 40], 1408887112064) # main's 'a' refers to the same object, same ID
>>> show()
[20, 30, 40] 1408887112064 # glbltest refers to the same object, same ID
It's a bit more obvious that the names are different if you just import the module and the module's a
can be referred to directly as glbltest.a
.
a = []
def fun():
global a
a = [20,30,40]
>>> import glbltest
>>> glbltest.a
[]
>>> a = 5 # main's a
>>> a
5
>>> glbltest.a # module's a
[]
>>> glbltest.fun()
>>> a # main's a doesn't change
5
>>> glbltest.a # module's a does.
[20, 30, 40]