我最近发现了一些有趣的行为让我想知道对象如何知道存在哪些全局变量。例如,假设我有一个文件" test.py":
globalVar = 1
toDelete = 2
class Test(object):
classVar = 3
def runTest1(self):
print globalVar
print toDelete
print missingVar
def runTest2(self):
print self.classVar
print toCreate
print missingVar
然后在交互式shell中我这样做:
>>> import test
>>> tester = test.Test()
>>> tester.runTest1()
1
2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "test.py", line 10, in runTest1
print missingVar
NameError: global name 'missingVar' is not defined
>>> tester.runTest2()
3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "test.py", line 14, in runTest2
print toCreate
NameError: global name 'toCreate' is not defined
没什么好奇怪的。然后我改变了#34; test.py&#34;的前几行。对此:
globalVar = 4
toCreate = 5
class Test(object):
classVar = 6
现在回到交互式shell:
>>> reload(test) # test = reload(test) gives the same result
<module 'test' from 'test.py'>
>>> tester.runTest1()
4
2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "test.py", line 10, in runTest1
print missingVar
NameError: global name 'missingVar' is not defined
>>> tester.runTest2()
3
5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "test.py", line 15, in runTest2
print missingVar
NameError: global name 'missingVar' is not defined
>>> dir(test)
['Test', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'globalVar', 'toCreate', 'toDelete']
所以tester
现在知道toCreate
,tester
本身就是在toDelete
创建之后出现的。它仍然知道>>> import sys
>>> import importlib
>>> del(sys.modules['test']) # remove cached version
>>> test = importlib.import_module('test') # same result if I don't have 'test = '
>>> tester.runTest1()
None
None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "test.py", line 10, in runTest1
print missingVar
NameError: global name 'missingVar' is not defined
>>> tester.runTest2()
3
None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "test.py", line 15, in runTest2
print missingVar
NameError: global name 'missingVar' is not defined
>>> dir(test)
['Test', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'globalVar', 'toCreate']
,因为重新加载模块显然不会影响已删除的全局变量。这有一个转折点:
sys.modules
从None
删除模块然后重新导入模块会导致所有全局变量变为test
。
同样令人感兴趣的是,如果我删除sys.modules['test']
和None
,它仍然会知道变量的值一段时间。过了一会儿(我假设模块被垃圾收集需要多长时间),值变为tester
。重新导入模块会导致垃圾收集(或正在发生的任何事情)立即发生。
那么{{1}}如何找出正在创建的新全局变量,然后一旦模块消失,为什么它仍然知道哪些变量存在,即使它不再知道它们持有什么值?
答案 0 :(得分:5)
任何非本地的名称(尚未在当前范围内分配)都被假定为全局名称。每次代码运行时都会查找该名称。
所以在 runtime 中,在全局命名空间中查找名称,这只是一个字典。如果此时名称不存在,则会引发NameError
异常。
拆卸功能时可以看到这个;使用dis
module时显示字节码:
>>> import dis
>>> def foo():
... bar = 'baz' # local
... bar # reference the local
... baz # reference something else; e.g. a global
...
>>> dis.dis(foo)
2 0 LOAD_CONST 1 ('baz')
3 STORE_FAST 0 (bar)
3 6 LOAD_FAST 0 (bar)
9 POP_TOP
4 10 LOAD_GLOBAL 0 (baz)
13 POP_TOP
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
bar
是一个本地(它在块中分配),而baz
是全局的。本地由LOAD_FAST
引用,而全局由LOAD_GLOBAL
引用。
为此,函数对象有一个function.__globals__
引用,将其链接到模块全局映射;请参阅datamodel documentation:
>>> foo.__globals__ is globals()
True
Python删除模块时清理全局;防止循环引用阻止终结全局变量在那时被反弹到None
(尽管这种行为changed in Python 3.4)。但是,如果您保留对tester
的引用,您的代码将会找到None
个值。
您的tester
实例仍在引用原始类及其方法,仍然通过其function.__globals__
引用引用其模块。因此,虽然您删除了对模块的sys.modules
引用,但触发了模块清理,但类方法仍然引用了全局字典。这个全局字典现在为每个全局字符保存None
个值。