Python puzzler: the import statement and global variables

Recently I’ve joined the BOS team in helping develop the next generation of the survey software. This time round were using Django (a Python-based web framework), PostgreSQL and Cassandra (a NoSQL database).

Although I’ve dabbled with Python briefly in the past this is my first substantial Python-based project. As a new Python developer I hit rather confusing bug in our code recently that concerned the Python import statement and global variables. It’s an interesting little oddity that I thought would be worth writing about.

Say we have a module (mod1.py) which contains a global dictionary object which needs to be initialised in a lazy fashion:


# mod1.py
DICT = {} # create a 'global' var
def init_dict():
    global DICT # make sure we are not referencing a local reference
    DICT = {'a' : '1', 'b' : 2} # reset or reassign variable
    print 'Dict initalized to length:',len(DICT) # everything looks good

 

Now we have another module (mod2.py) referencing this first:


# mod2.py
from mod1 import DICT, init_dict
print 'Initial length of dict:',len(DICT)
init_dict() # initialise the DICT variable
print 'New length of dict:',len(DICT)

Seems valid. So what output would you expect when we run this code?

Huh? We confirm the initial state, initialised the DICT variable, re-read it’s contents, and still get the initial state. So what should we do to correct this?


# mod2-corrected.py
import mod1
print 'Initial length of dict:',len(mod1.DICT)
mod1.init_dict()
print 'New length of dict:',len(mod1.DICT)

 

 

 

It looks similar but this time round we import the module itself  rather then the DICT directly, and reference DICT via the module reference. After much web searching I finally found the answer – that global variables are confined to their module’s namespace (i.e. they are not shared between modules despite the use of the term ‘global’). Actually I only found this out after I copied the text of this blog post into a new stackoverflow question and it automatically pointed me at a previous answer, but as it stumped me for quite a while I thought it is worth highlighting to others.