Two instances of the same Python module?

I created a Python module with a single function that just prints ‘a!’. I opened up the Python interpreter and imported the module in 2 different syntaxes

>>> import a
>>> from a import func
>>> func()
a!
>>> a.func()
a!

At this point I changed func to print something else, then evaluates again

>>> func()
a!
>>> a.func()
a!

This is expected of course as the module was not reloaded. Then I reloaded the module and expected both functions to update, however:

>>> reload(a)
<module 'a' from 'a.py'>
>>> a.func()
aasd!
>>> func()
a!

Only a.func seems to update. I always thought that Python keeps only a single instance of the same module, but now there appears to be two. I did further testing in order to verify my claim, and added a print statement at top level of the module, then restarted the interpreter and imported again:

>>> import a
module imported
>>> import a
>>> from a import func

This confuses me even more as I expected to see ‘module imported’ twice. The third experiment that I did was the global variable experiment:

>>> import a
module imported
>>> from a import GLOBAL_VAR
>>> GLOBAL_VAR = 5
>>> a.GLOBAL_VAR
1
>>> GLOBAL_VAR
5
>>> GLOBAL_VAR is a.GLOBAL_VAR
False

So there’s a single instance of the module, but different instances of the objects inside? How is it possible to implement Gevent’s monkey patching with such behaviour?

Best answer

A module, once it’s imported, is just another python object. So seeing the following example, your results should not surprise you at all:

x = SomeObject()
x.y = 1
a = x.y
x.y = 2
print(a) #a is still 1, not 2

When you do from module import name, a variable name is created in the current namespace which holds a reference to the imported thing (be it a package/module/class/whatever). It’s syntactic sugar for the following:

import module
name = module.name

Now, when you reload module, you obviously don’t update the reference name holds.

Concerning your second experiment, a module is cached in sys.modules after the import; subsequent imports will take advantage of the cache. Thus all code that is directly at the module level, like your print, will only get executed on the first import.