I am trying to understand the behavior of variables shared across modules. My impression was a global variable in one module could be accessed in another, and sometimes this seems to be what happens, but sometimes it is not. (I recognize global variables are usually not the right tool, but there are situations where their use is appropriate.)
Here is a code example to illustrate my confusion:
shared.py
globalvar=0 # a global scalar variable
globalarray1=[1,2,3] # a global array modified by .append()
globalarray2=['A','B','C'] # a global array modified by reassignment
def set_globals():
global globalvar
global globalarray1
global globalarray2
globalvar=1
globalarray1.append(4)
globalarray2=['a','b','c']
print("set_globals: globalvar="+str(globalvar))
print("set_globals: globalarray1="+str(globalarray1))
print("set_globals: globalarray2="+str(globalarray2))
module.py
from paas.shared import *
def _main_():
global globalvar
global globalarray1
global globalarray2
set_globals()
print("main: globalvar="+str(globalvar))
print("main: globalarray1="+str(globalarray1))
print("main: globalarray2="+str(globalarray2))
_main_()
When I run the program, I get the following output:
set_globals: globalvar=1
set_globals: globalarray1=[1, 2, 3, 4]
set_globals: globalarray2=['a', 'b', 'c']
main: globalvar=0
main: globalarray1=[1, 2, 3, 4]
main: globalarray2=['A', 'B', 'C']
So
Can someone explain what is going on? I have only seen fairly simple tutorial-type documentation about global variables; if there is documentation that describes what is happening here I would appreciate a pointer.
ADDED NOTE: This is not related to the way a local variable in a function hides a global variable with the same name; those are two different variables with the same name and different initial values. It does appear that the import creates a new module variable with the same name, but it starts with the initial value of the variable of the same name from the imported module.
What you're observing stems from Python's behavior with respect to variables, imports, and mutability. Let's break it down:
globalarray1
is a list, which is a mutable object in Python. When you do globalarray1.append(4)
, you are mutating the original list object.globalarray1
in module.py
using from paas.shared import *
, you are importing a reference to that list object. Therefore, when you mutate the list in shared.py
, that mutation is visible in module.py.globalarray2=['a','b','c']
inside set_globals
reassigns the variable to a new list object. However, module.py
still references the original list object.globalvar
, when you set globalvar=1
inside set_globals
, you're modifying the value of the variable within the scope of shared.py
. However, in module.py
, the globalvar
still references the old value (0).from paas.shared import *
, you're importing the names from shared.py
into module.py
. If you later rebind one of these names in shared.py
(as you do with globalvar
and globalarray2
), the binding in module.py
remains unchanged.How to fix this and see consistent behavior:
You should access variables from the shared module using an explicit module prefix. This way, you're always referring to the most recent value of the variable in the shared module. Here's how you can change your module.py
, let's call it module2.py
:
import paas.shared as shared
def _main_():
shared.set_globals()
print("main: globalvar="+str(shared.globalvar))
print("main: globalarray1="+str(shared.globalarray1))
print("main: globalarray2="+str(shared.globalarray2))
_main_()
With this change, the print statements in _main_()
will refer directly to the shared module's versions of the variables, and you'll see consistent behavior between set_globals
and _main_()
.
Now let's compare the output. Here's module.py
(unmodified from above):
$ python module.py
set_globals: globalvar=1
set_globals: globalarray1=[1, 2, 3, 4]
set_globals: globalarray2=['a', 'b', 'c']
main: globalvar=0
main: globalarray1=[1, 2, 3, 4]
main: globalarray2=['A', 'B', 'C']
And now the new code using an explicit import (module2.py
):
$ python module2.py
set_globals: globalvar=1
set_globals: globalarray1=[1, 2, 3, 4]
set_globals: globalarray2=['a', 'b', 'c']
main: globalvar=1
main: globalarray1=[1, 2, 3, 4]
main: globalarray2=['a', 'b', 'c']