Python内部 - 对象如何知道全局变量?

时间:2014-06-03 16:18:58

标签: python python-2.7 global-variables python-import python-module

我最近发现了一些有趣的行为让我想知道对象如何知道存在哪些全局变量。例如,假设我有一个文件" 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现在知道toCreatetester本身就是在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}}如何找出正在创建的新全局变量,然后一旦模块消失,为什么它仍然知道哪些变量存在,即使它不再知道它们持有什么值?

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个值。