简体   繁体   English

python如何评估模块执行?

[英]How does python evaluate Module execution?

I have two files in the same directory one that contains the contents of my program poker.py and the other that has test cases poker_test.py . 我在同一目录中有两个文件,一个包含我的程序poker.py的内容,另一个包含测试用例poker_test.py

In poker_test.py I execute the following commands to run the test cases: poker_test.py我执行以下命令来运行测试用例:

import unittest
import poker


class PokerTest(unittest.TestCase):
      (...)

Then at the end of poker.py while I'm developing i have the following commnands: 然后在poker.py结束时,我正在开发我有以下commnands:

if __name__ == "__main__":
    #imports Test case and unittest
    from poker_test import *
    unittest.main() 

Everything works just fine(for now) and this setup is great for iterative development. 一切都很好(现在),这个设置非常适合迭代开发。 My question is how does python evaluate poker.py when I run it, given that poker_test.py depends on poker.py and vice versa? 我的问题是,当我运行它时,python如何评估poker.py ,因为poker_test.py依赖于poker.py ,反之亦然?

I have a rough idea, but was wondering what the "official" answer is. 我有一个粗略的想法,但想知道“官方”答案是什么。

Thanks, -M 谢谢,-M

In terms of whether you should do it, as Alex says, avoid it at all costs. 至于你是否应该这样做,正如亚历克斯所说,不惜一切代价避免它。 Cyclical imports are a state of sin. 周期性进口是一种罪恶状态。

With that aside, it's interesting to look at what's happening (roughly - it looks like the module import mechanism is an area that gets tweaked from version to version in Python . My main source for this was the Python 3.4.2 docs on the importing system) 除此之外,看看正在发生的事情很有意思(粗略地说 - 看起来模块导入机制是一个在Python中从一个版本调整到另一个版本的区域 。我的主要来源是导入系统上的Python 3.4.2文档)

When the line within poker_test.py : poker_test.py的行:

import poker

gets executed, the system first checks if the module is already loaded. 执行后,系统首先检查模块是否已加载。 Loaded modules live in a dictionary called sys.modules . 加载的模块存在于名为sys.modules的字典sys.modules

  • If the module being imported is in sys.modules already, any reference to poker in poker_test.py just points to that namespace. 如果正在导入的模块已经在sys.modulespoker_test.pypoker任何引用poker_test.py指向该命名空间。 (Note that in the case of cyclical imports, the module might already be added to sys.modules , but the populating of the namespace may not be fully over. Execution of that module might be paused for the moment at the line that says import this_or_that_other_module ) (请注意,在循环导入的情况下,模块可能已经添加到sys.modules ,但是命名空间的填充可能还没有完全结束。该模块的执行暂时可能暂停在import this_or_that_other_module的行上)
  • If the module is not there, then the system creates a new namespace, adds it to sys.modules , looks for the code that is associated with the poker module (in this case, living in poker.py ) and begins to execute it, putting all the variables in the newly created namespace. 如果模块不在那里,那么系统会创建一个新的命名空间,将它添加到sys.modules ,查找与poker模块相关的代码(在这种情况下,生活在poker.py )并开始执行它,将所有变量放在新创建的命名空间中。

So you'd think poker.py gets run once, and poker_test.py gets run once and already notices that poker is a loaded module, so the importing ends there. 所以你认为poker.py会运行一次,而poker_test.py会运行一次,并且已经注意到poker是一个加载模块,因此导入结束。 Except... 除了...

When a module is run as the original script, it gets registered as __main__ in sys.modules , instead of its actual name. 当模块作为原始脚本运行时,它将在sys.modules注册为__main__ ,而不是其实际名称。

So poker.py is going to be called the __main__ module, and as a result, when poker_test tries running import poker , it can't find poker under sys.modules . 所以poker.py将被称为__main__模块,因此,当poker_test尝试运行import poker ,它无法在sys.modules下找到poker poker will be loaded twice, once as __main__ and again as poker . poker 被加载两次,一次作为__main__ ,再次作为poker Cyclical imports are frowned upon, but cyclical imports of the __main__ module is downright condemned because of this issue of creating two identical(ish) namespaces and the potential weird errors that can result. 循环导入是不受欢迎的,但__main__模块的周期性导入是彻头彻尾的谴责,因为这个问题创建了两个相同的(ish)命名空间以及可能导致的潜在奇怪错误。

There are two more complications in your code. 您的代码中还有两个复杂问题。

1) from poker_test import * 1) from poker_test import *

Because you're doing an import * , instead of putting all the variables created from poker_test in its own namespace, it gets thrown into the __main__ namespace. 因为你正在进行import * ,而不是将从poker_test创建的所有变量放在它自己的命名空间中,它会被抛入__main__命名空间。

2) if __name__=='__main__': 2) if __name__=='__main__':

Because you're only importing from poker_test if the module is the main script being executed, the Python interpreter will not touch that line when poker gets imported from poker_test . 因为你只是从poker_test导入,如果模块是正在执行的主要脚本,当从poker_test导入poker时,Python解释器不会触及该行。 So your code isn't really looping conceptually. 所以你的代码在概念上并没有真正循环。 poker as __main__ imports poker_test which imports poker and stops there. poker作为__main__进口poker_test进口poker并停在那里。 Simple! 简单!

...so let's not do circular imports. ......所以我们不要做循环进口。

Some reference material: 一些参考资料:

Official Python 3.4.2 docs on the import system 关于导入系统的官方Python 3.4.2文档

2008 comp.lang.python discussion on cyclical imports 2008年comp.lang.python关于周期性进口的讨论

It's always soundest to avoid circular dependencies like the one you've created here. 避免像你在这里创建的循环依赖一样,总是最难的。 However you luck out because the from poker_test import * comes at the very end of poker.py , ie, after the latter has defined all it does define, so it can be imported in a good state by the circularly-dependent poker_test.py . 但是你很幸运,因为from poker_test import *来自poker.py ,即,在后者定义了它所定义的所有内容之后,所以它可以通过循环依赖的poker_test.py以良好的状态导入。

However, while this happens to work in current Python version,s it's not guaranteed by the language spec. 然而,虽然这恰好在当前的Python版本中有效,但是语言规范并不能保证这一点。 For solidity, break the circular dependency, eg as follows: 为了坚固,打破循环依赖,例如如下:

  • move all the "substantial" contents of poker.py before the if __name__ check to eg _poker.py 移动所有的“实质性”内容poker.py之前, if __name__检查,如_poker.py
  • in poker.py , just do from _poker import * poker.py ,只需from _poker import *
  • in poker_test.py , instead of import poker , use import _poker as poker poker_test.py ,使用import _poker as poker ,而不是import poker import _poker as poker

This way, your dependency graph becomes acyclical, and as a consequence your code will work as intended in any correct version of Python, including hypothetical future ones:-). 这样,您的依赖图变为非周期性的,因此您的代码将在任何正确版本的Python中按预期工作,包括假设的未来:-)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM