gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-28 21:54:34 25 4
gpt4 key购买 nike

我最近发现了一些有趣的行为,这让我想知道对象如何知道存在哪些全局变量。例如,假设我有一个文件“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

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

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

同样有趣的是,如果我删除 testsys.modules['test'],它仍然会暂时知道变量的值。片刻之后(我假设这是模块被垃圾回收所花费的时间),值变为 None。重新导入模块会导致垃圾收集(或任何正在发生的事情)立即发生。

那么 tester 如何找到正在创建的新全局变量,然后一旦模块消失,为什么它仍然知道存在哪些变量,即使它不再知道它们持有什么值?

最佳答案

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

因此在运行时 名称在全局命名空间中查找,它只是一个字典。如果该名称当时不存在,则会引发 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 是局部的(它被分配到 block 中),而 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 引用,触发了模块清理,但 globals 字典 仍被类方法引用。这个全局字典现在包含每个全局的 None 值。

关于Python 内部 - 对象如何知道全局变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24020108/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com