Two instances of the same Python module?

6.1k views Asked by At

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?

4

There are 4 answers

0
l4mpi On 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.

0
Bleeding Fingers On

I thing that I just confirmed is that a particular session of Python ensures that there is only one instance of a particular module (more specifically a file path).

Proof(single Python session used):

foobar.py:

a = 123
def foo(): print a

Python:

>>> from foobar import foo
>>> foo()
123

Changing foobar.py:

a = 1234
def foo(): print a;print a

Python:

>>> reload(foobar)
<module 'foobar' from 'c:\foobar.py'>
>>> foobar.foo()
1234
1234
>>> foo()
1234
>>> id(sys.modules[foo.__module__]) == id(sys.modules[foobar.foo.__module__])
True

The only thing that is causing different outputs is the code object, (foo.__globals['a'] and foobar.foo.__globals__['a'] are the same)

>>> id(foo.__code__) == id(foobar.foo.__code__)
False

No idea about Gevent monkey patching.

0
thefourtheye On

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

3
fengsp On

In Python, variables are names, not containers. Everything is an object, everything is a reference, while variables are neither. Variables refer to objects and namespaces map names to objects. You can use module to organize code, every module is executed on first import.

So after you import a as b, b refer to object a, a will stay the same unless you modify object a(if a is mutable). When you reload(a), you just assign one new object to a variable, that has nothing to do with b at all.

>>> a = 1
>>> b = a
>>> a = 2
>>> print b
1

>>> import a
>>> from a import func
>>> id(func) 
4324575544
>>> id(a.func)
4324575544
>>> reload(a)
<module 'a' from 'a.py'>
>>> id(a.func)
4324576624
>>> id(func)
4324575544