提问人:Tortar 提问时间:8/24/2022 最后编辑:Tortar 更新时间:6/6/2023 访问量:267
导入封闭命名空间时,全局变量的更改会丢失
Change of a global variable gets lost when importing the enclosing namespace
问:
我在玩范围和命名空间时,我发现了一个奇怪的行为,我不知道如何解释。假设我们有一个名为 new_script.py 的文件,里面有
a = 0
def func():
import new_script #import itself
new_script.a += 1
print(new_script.a)
func()
print(a)
执行时打印
1
1
2
0
没想到最后打印了这个数字.据我了解,它打印前两个执行 self-import 语句递增全局,然后它打印,因为它从函数内部再次递增全局,但为什么最后一次打印不是?0
1
a
2
a
0
2
答:
TL的;DR:你有两个不同的变量和 .你只改变.__main__.a
new_script.a
new_script.a
要通过以下方式进行跟踪:
a = 0
在模块中定义一个变量。a
__main__
def func(): ...
在模块中定义一个函数。func
__main__
func()
在模块中调用此函数。在功能中:__main__
import new_script
imports 模块,其中:new_script
a = 0
def func(): ...
在模块中定义 和 ( 和a
func
new_script
new_script.a
new_script.func
)
func()
来自模块的调用。在功能中:func
new_script
import new_script
好吧,我们已经导入了它,所以我们不会再次导入它
new_script.a += 1
print(new_script.a)
递增并打印它(我们的第一个)。然后new_script.a
1
print(a)
打印(又名)从模块(我们的第二个)a
new_script.a
new_script
1
)
new_script
完成了。
返回执行:__main__
new_script.a += 1
print(new_script.a)
第二次递增并打印它(我们的 )。new_script.a
2
最后:
print(a)
从未更改过的印刷(又名 )(所以a
__main__.a
0
)
好吧,这导致了一个非常有趣的 rabit 洞。所以谢谢你。
以下是关键点:
- 导入不会递归。如果导入一次,它将执行模块级代码,但如果再次导入,则不会再次执行。因此,您只能看到 4 个值。
- 进口是单例。如果您尝试以下代码:
# singleton_test.py
import singleton_test
def func():
import singleton_test #import itself
print(singleton_test.singleton_test == singleton_test)
func()
它将打印:
True
True
- 导入的模块单例版本与模块的原始运行版本不同
考虑到这一点,我们可以通过用更多的注释来丰富你的代码,特别是使用包含当前模块名称的注释,如果当前模块是最初运行的模块,这将是:__name__
__main__
a = 0
print("start", __name__)
def func():
print("Do import", __name__)
import new_script #import itself
new_script.a += 1
print(new_script.a, "func", __name__)
func()
print(a, "outr", __name__)
这将打印
start __main__
Do import __main__
start new_script
Do import new_script
1 func new_script
1 outr new_script
2 func __main__
0 outr __main__
这很好地表明,假设导入的模块是单例(但不是运行的模块),您
- 在模块内的函数内递增值后,首先在函数中打印 1
- 然后在导入模块的末尾打印 1
- 然后,在原始运行代码上将单例上的值递增后打印 2
- 最后,您为最初运行但尚未触及的未更改的外部模块打印 0。
评论
0
print(a)
a
__main__