同一个Python模块的两个实例?

时间:2013-12-16 07:49:20

标签: python python-2.7 python-module python-internals

我创建了一个带有单个函数的Python模块,只打印'a!'。我打开了Python解释器并用2种不同的语法导入了模块

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

此时我更改了func以打印其他内容,然后再次评估

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

当然,由于模块未重新加载,因此可以预期。然后我重新加载模块并期望两个函数都更新,但是:

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

只有a.func似乎更新。我一直认为Python只保留同一模块的单个实例,但现在似乎有两个。我做了进一步的测试以验证我的声明,并在模块的顶层添加了一个print语句,然后重新启动了解释器并再次导入:

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

这让我更加困惑,因为我希望看到“模块导入”两次。我做的第三个实验是全局变量实验:

>>> 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

所以有一个模块的实例,但内部对象的不同实例?如何以这种行为实施Gevent's monkey patching

4 个答案:

答案 0 :(得分:7)

一个模块一旦导入,就只是另一个python对象。因此,看到以下示例,您的结果应该不会让您感到惊讶:

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

执行from module import name时,会在当前命名空间中创建变量name,该变量包含对导入事物的引用(无论是包/模块/类/其他)。它是以下的语法糖:

import module
name = module.name

现在,当您重新加载module时,您显然不会更新引用name保留。

关于您的第二个实验,导入后,模块会在sys.modules中缓存;后续导入将利用缓存。因此,直接在模块级别的所有代码(例如print)将仅在第一次导入时执行。

答案 1 :(得分:2)

重新加载模块时,其中的所有功能都会重新加载到当前模块中。但是当您从模块导入特定函数时,它将成为当前模块的本地函数。因此,改变一个不会影响另一个。

检查出来:

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)

<强>输出

{'__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在CPython实现中打印内存中对象的地址

答案 2 :(得分:0)

我刚刚确认的事情是Python的特定会话确保只有一个特定模块的实例(更具体地说是文件路径)。

证明(使用单个Python会话):

foobar.py:

a = 123
def foo(): print a

的Python:

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

更改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

导致不同输出的唯一因素是code对象,(foo.__globals['a']foobar.foo.__globals__['a']是相同的)

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

不知道Gevent猴子补丁。

答案 3 :(得分:-1)

在Python中,变量是名称,而不是容器。一切都是一个对象,一切都是参考,而变量都不是。变量引用对象和名称空间将名称映射到对象。您可以使用模块来组织代码,每个模块都会在首次​​导入时执行。

因此,在导入a b后,b引用对象a,a将保持不变,除非您修改对象a(如果a是可变的)。当您重新加载(a)时,您只需为变量分配一个新对象,这与b完全无关。

>>> 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
相关问题