Python内部 - 对象如何知道全局变量? [英] Python internals - How do objects know about global variables?

查看:147
本文介绍了Python内部 - 对象如何知道全局变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我发现了一些有趣的行为,让我想知道一个对象如何知道存在哪些全局变量。例如,假设我有一个test.py文件:

  globalVar = 1 
toDelete = 2
$ b $ 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中,我这样做:

 >> ;>导入测试
>>> tester = test.Test()
>>> tester.runTest1()
1
2
Traceback(最近一次调用的最后一个):
在< module>中的第1行文件< stdin>
文件test.py,第10行,在runTest1中
print missingVar
NameError:全局名称'missingVar'未定义
>>> tester.runTest2()
3
Traceback(最近一次调用的最后一个):
在< module>中,第1行的文件< stdin>
文件test.py,第14行,在runTest2中
print toCreate
NameError:全局名称'toCreate'未定义

没什么奇怪的。然后我将test.py的前几行更改为:

  globalVar = 4 
toCreate = 5

class Test(object):
classVar = 6

现在回到交互式shell:

 >>> reload(test)#test = reload(test)给出相同的结果
>>> tester.runTest1()
4
2
Traceback(最近一次调用的最后一个):
在< module>中的第1行文件< stdin>
文件test.py,第10行,在runTest1中
print missingVar
NameError:全局名称'missingVar'未定义
>>> tester.runTest2()
3
5
Traceback(最近一次调用的最后一个):
在< module>中的第1行文件< stdin>
文件test.py,第15行,在runTest2中
print missingVar
NameError:未定义全局名'missingVar'
>>> dir(test)
['Test','__builtins__','__doc__','__file__','__name__','__package__','globalVar','toCreate','toDelete']

因此 tester 现在知道 toCreate ,它出现在 tester 本身之后。它仍然知道 toDelete ,因为重新加载模块显然不会影响已删除的全局变量。这里有一个转折:

 >>> import sys 
>>> import importlib
>>> del(sys.modules ['test'])#删除缓存版本
>>> test = importlib.import_module('test')#如果我没有'test ='
>>> tester.runTest1()
None
None
Traceback(最近一次调用的最后一次):
在< module>中的第1行文件< stdin>
文件test.py,第10行,在runTest1中
print missingVar
NameError:全局名称'missingVar'未定义
>>> tester.runTest2()
3
None
Traceback(最近一次调用的最后一个):
在< module>中的第1行文件< stdin>
文件test.py,第15行,在runTest2中
print missingVar
NameError:未定义全局名'missingVar'
>>> dir(test)
['Test','__builtins__','__doc__','__file__','__name__','__package__','globalVar','toCreate']

sys.modules 删除模块,然后重新导入模块将导致所有全局变量变成 None



另外感兴趣的是,如果我删除 test sys.modules ['test'] ,它仍然知道变量的值。经过一段时间(我假设模块被垃圾收集需要多长时间),值变为 None 。重新导入模块会导致垃圾回收(或者其他情况)立即发生。



那么 tester 发现一个新的全局变量被创建,然后一旦模块不存在,为什么它仍然知道哪些变量存在,即使它不知道它们保存了什么值?

解决方案

任何非本地名称(尚未分配给当前范围)都被假定为全局名称。每次运行代码时都会查找该名称。



因此,在运行时处,名称在全局名称空间中查找,字典。如果此时名称不存在,则会引发 NameError 异常。



您可以看到你拆解一个函数;使用 dis 模块时显示字节码

 >>> import dis 
>>> def foo():
... bar ='baz'#local
... bar#引用本地
... baz#引用别的东西;例如全球
...
>>> DISDIS(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(无)
17 RETURN_VALUE

bar 是本地的(它被分配到了块中),而 baz 是全球性的。本地被 LOAD_FAST 引用,而全局引用 LOAD_GLOBAL



为此,函数对象有一个函数.__全局变量__ 引用将它链接到模块全局映射;请参阅数据模型文档中的用户定义函数部分>:

 >>> foo .__ globals__是globals()
True

Python也清理全局变量删除模块时;为防止循环引用阻止敲定全局变量当时被反弹到 None (尽管此行为在Python 3.4中更改)。如果您保留对 tester 的引用,您的代码将会找到那些 None 值。



您的 tester 实例,仍然引用原始类及其方法,仍然通过它们的函数引用它们的模块.__ globals__ 参考。因此,尽管您已经删除了模块的 sys.modules 引用,但触发了模块清理,但类方法仍然引用了 globals dictionary 。这个全局变量字典现在为每个全局保存 None 值。


I recently discovered some interesting behavior that left me wondering about how an object knows about which global variables exist. For example, suppose I have a file "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

Then in an interactive shell I do this:

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

Nothing surprising. Then I change the first few lines of "test.py" to this:

globalVar = 4 
toCreate = 5

class Test(object):
    classVar = 6

Now back to the interactive 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']

So tester now knows about toCreate, which showed up after tester itself was created. It still knows about toDelete because reloading a module apparently doesn't affect a global variable that was deleted. Here comes a twist:

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

Deleting the module from sys.modules and then reimporting the module results in all the global variables becoming None.

Also of interest is that if I delete test and sys.modules['test'], it still knows about the values of the variables for a while. After a little while (which I assume is how long it takes for the module to be garbage collected) the values become None. Reimporting the module causes the garbage collection (or whatever is going on) to happen immediately.

So how does tester find out about a new global variable being created, and then once the module is gone why does it still know which variables existed even though it no longer knows what values they held?

解决方案

Any name that is not a local (has not been assigned to in the current scope) is instead assumed to be a global. The name is looked up every time the code runs.

So at runtime the name is looked up in the global namespace, which is just a dictionary. If the name doesn't exist at that time, a NameError exception is raised.

You can see this when you disassemble a function; bytecode is shown when using the 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 is a local (it was assigned to in the block), while baz is a global. The local is referenced by LOAD_FAST, while the global is referenced by LOAD_GLOBAL.

To do this, function objects have a function.__globals__ reference linking it to the module globals mapping; see the User-defined functions section in the datamodel documentation:

>>> foo.__globals__ is globals()
True

Python also cleans up globals when deleting modules; to prevent circular references holding up finalisation globals are rebound to None at that time (although this behaviour changed in Python 3.4). If you however maintain a reference to tester, your code will find those None values.

Your tester instance, still referencing the original class and it's methods, are still referencing their module through their function.__globals__ references. So, although you deleted the sys.modules reference to the module, triggering the module cleanup, the globals dictionary is still referenced by the class methods. This globals dictionary now holds None values for each global.

这篇关于Python内部 - 对象如何知道全局变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆