Python 循环输入混淆
我有三个文件: 1) 1.py 2) 2.py 3) 3.py 当我运行命令python two.py时,我得到:Python 循环输入混淆,python,python-2.7,Python,Python 2.7,我有三个文件: 1) 1.py 2) 2.py 3) 3.py 当我运行命令python two.py时,我得到: [1, 2] [1, 2] [1, 2, 3] [1, 2, 3] 当我运行命令python one.py时,我得到: [1, 2] [1, 2] [1, 2, 3] [1, 2, 3] 请任何人解释一下为什么我会得到这个输出?我越深入这个兔子洞,它就越疯狂。在玩了一会儿之后,我想我对这里到底发生了什么有了一些想法,尽管我仍然不清楚其中的一些原因 import x和from
[1, 2]
[1, 2]
[1, 2, 3]
[1, 2, 3]
当我运行命令python one.py时,我得到:
[1, 2]
[1, 2]
[1, 2, 3]
[1, 2, 3]
请任何人解释一下为什么我会得到这个输出?我越深入这个兔子洞,它就越疯狂。在玩了一会儿之后,我想我对这里到底发生了什么有了一些想法,尽管我仍然不清楚其中的一些原因
import x
和from x import y
之间存在根本区别。在这两种情况下,将运行文件x
,并执行其中的代码-包括任何print
语句,这是有意义的。如果使用import x
,则执行文件x
,保留其命名空间(包括任何函数或变量定义),并在当前文件的命名空间中包含指向模块x
的链接。如果使用“从x导入y”中的,则执行文件x
,然后将y
的副本粘贴到当前文件的命名空间中。你基本上是在那个时间点拍下两张
的快照;two
以后所做的任何更改都不会反映在two
中
让我们来看看翻译:
>>> execfile("two.py") # This lets us run the file and keep the namespace
[1, 2]
[1, 2]
>>> id(test)
47312858429416
>>> id(three.test)
47312858429128
在本例中,test
是由two.py
创建的变量。由于ID不同,我们可以看到three
模块创建了test
的副本,而不是引用实际对象
现在,我们可以遵循python two.py
的程序逻辑。我们首先创建test=[1,2]
。然后执行three.py
。此时,我们有来自两个导入测试的行,它将自动运行two.py
,这将执行print
语句(导致[1,2]
)。这还将制作一份test
的本地副本,供three
使用。然后,three
模块继续并追加3,但它只在其本地副本test
中追加3。然后我们返回到two.py
继续执行。当我们打印test
时,我们正在打印它的本地副本,three
从未修改过。这就是我们第二次得到[1,2]
的原因
所以这里是它变得棘手的地方
再次查看上面的execfile
代码,并将其与下面的代码进行比较,在下面的代码中,我没有执行python two.py
,而是运行import two
:
>>> import two
[1, 2, 3]
>>> two.test
[1, 2, 3]
>>> two.three.test
[1, 2, 3]
>>> id(two.test)
47787298457576
>>> id(two.three.test)
47787298457576
当我看到它的时候,我的大脑爆炸了,原因有两个。首先:我们不再打印两次,只打印一次。第二:three
模块不再有单独的test
副本;它现在指的是与two
完全相同的对象
我得出的结论是如果导入two.py
而不是将其作为主文件执行,Python将自动解析循环引用,将test
对象链接在一起,并防止two
多次执行。当two
尝试导入时,two
检测到它将再次被导入,而不是再次执行该代码,它只需将它的test
副本交给twree
。不管怎么说,我就是这样理解的
考虑这一点是有意义的,因为否则您可能会得到一个无限递归循环。我有点惊讶,Python不仅仅因为用户尝试这种循环怪癖而打了他们一巴掌,因为它会抓住其他类似的导入问题,但这也是一种很好的处理方法
但这解释了python one.py的不同行为。如果您查看两个导入测试中的发生了什么,我们似乎看到的是two
中的“导入”行为,而不是“执行”行为。完成该行代码后,two
包含一个test
对象,该对象包含[1,2,3]
,并打印了一次。然后将其复制到one
的命名空间中,并再次打印。这就是为什么我们会有这种奇怪的行为
So tl;dr:Python将根据您是正常执行文件还是导入文件来执行不同的代码。我们运行two
的方式不同,这一切都不同。但是不要依赖Python以合理的方式解析循环引用;对我来说,这就像是在玩系统游戏,大多数时候会有更好的方法来设计你的程序,而不必处理这种混乱
有了这个,我要休息一下。我非常好奇您的用例是什么。@TheSoundDefense感谢您指出我的错误。我正在读一篇关于flask项目组织的文章()。它们有相似的代码。我理解python two.py
的行为,但我不理解test
的新值如何一直到one.py
。我不想给出半个答案://声音防御如果答案不太长,请与我分享你的智慧:)我认为你的答案会帮助我。谢谢!这就像火箭科学:)@warmsspringwinds这是迄今为止我所做的最令人困惑的与Python相关的事情。但至少我表现出了我的理智。
>>> import two
[1, 2, 3]
>>> two.test
[1, 2, 3]
>>> two.three.test
[1, 2, 3]
>>> id(two.test)
47787298457576
>>> id(two.three.test)
47787298457576