Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 773
reish Avatar asked Dec 16 '13 07:12

reish


2 Answers

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.

like image 159
l4mpi Avatar answered Sep 24 '22 22:09

l4mpi


When you reload a module, all the functions in that are reloaded in the current module. But when you import a specific function from a module, it becomes local to the current module. So, changing one will not affect the other.

Check this out:

import math
from math import factorial
print locals()

print id(math.factorial), id(factorial)

math.factorial, factorial = 0, 1
print id(math.factorial), id(factorial)

reload(math)
print id(math.factorial), id(factorial)

from math import factorial
print id(math.factorial), id(factorial)

Output

{'__builtins__': <module '__builtin__' (built-in)>,
 '__file__': '/home/thefourtheye/Desktop/Test.py',
 '__package__': None,
 'factorial': <built-in function factorial>, # Factorial is in the local context
 '__name__': '__main__',
 '__doc__': None,
 'math': <module 'math' (built-in)>}
39346504 39346504
33545712 33545688
39346504 33545688
39346504 39346504

id prints the address of the object in memory in CPython implementation

like image 40
thefourtheye Avatar answered Sep 26 '22 22:09

thefourtheye