8

我用一个函数创建了一个 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 只保留同一个模块的一个实例,但现在似乎有两个。为了验证我的声明,我做了进一步的测试,并在模块的顶层添加了一个打印语句,然后重新启动解释器并再次导入:

>>> 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 的猴子补丁?

4

4 回答 4

8

一个模块,一旦被导入,就只是另一个 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,只会在第一次导入时执行。

于 2013-12-16T08:10:59.073 回答
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 实现中打印内存中对象的地址

于 2013-12-16T07:53:26.150 回答
0

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

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

foob​​ar.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猴子补丁。

于 2013-12-16T08:38:57.357 回答
-1

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

因此,在您将 a 作为 b 导入后,b 引用对象 a,除非您修改对象 a(如果 a 是可变的),否则 a 将保持不变。当你 reload(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
于 2013-12-16T09:49:34.320 回答