我正在玩范围和命名空间,我发现了一个奇怪的行为,我不知道如何解释。假设我们有一个名为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
没想到数字的最后一次打印0
.据我了解,它在执行 self-import 语句1
打印前两个,递增全局a
,然后打印2
,因为它再次从函数内部递增全局a
,但是为什么最后一次打印是0
而不是2
?
TL;DR:你有两个不同的变量__main__.a
和new_script.a
。你只改变new_script.a
.
要通过以下方式追踪它:
a = 0
定义__main__
模块中的变量a
。
def func(): ...
定义__main__
模块中的函数func
。
func()
在模块中调用此函数__main__
。在功能中:
import new_script
导入new_script
模块,其中:
a = 0
def func(): ...
定义new_script
模块中的a
和func
(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)
从new_script
模块打印a
(又名new_script.a
)(我们的第二个1
)
new_script
完了。 回到__main__
中的执行:
new_script.a += 1
print(new_script.a)
第二次递增new_script.a
并打印它(我们的2
)。
最后:
print(a)
印刷a
(又名__main__.a
)从未改变过(所以0
)
好吧,这导致了一个非常有趣的兔子洞。所以谢谢你。
以下是关键点:
- 导入不会递归。如果导入一次,它将执行模块级代码,但如果再次导入,则不会再次执行。因此,您只能看到 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。