关于python中模块的环状引用(circular imports)

本文深入探讨了Python中的导入机制,包括'import'和'from...import...'语句的工作原理,解释了模块如何被加载和执行的过程,以及在不同场景下模块间的相互引用是如何处理的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从stackoverflow上面copy过来的

1、

Imports are pretty straightforward really. Just remember the following:

'import' and 'from xxx import yyy' are executable statements. They execute when the running program reaches that line.

If a module is not in sys.modules, then an import creates the new module entry in sys.modules and then executes the code in the module. It does not return control to the calling module until the execution has completed.

If a module does exist in sys.modules then an import simply returns that module whether or not it has completed executing. That is the reason why cyclic imports may return modules which appear to be partly empty.

Finally, the executing script runs in a module named __main__, importing the script under its own name will create a new module unrelated to __main__.

Take that lot together and you shouldn't get any surprises when importing modules.


‘import’和‘from xxx import yyy' 是可执行语句,每当程序运行到他们所在的哪一行他们就执行,如果import的模块在sys.modules中未找到,import语句就在sys.modules中会创建一个新的模块,此时这个模块就已经存在于sys.modules中了,但是他的__dict__还是空的,紧接着会执行这个模块中的语句,以填充他的__dict__,直到这个模块执行结束,才会返回到刚才的import语句继续执行;如果import的模块sys.modules中已经存在,那么import语句将会直接返回这个模块而不论他是否已经执行完毕,这就是为什么cyclic imports返回的模块有时候会是空的


最后,执行脚本在命名为__main__的模块中运行,导入一个模块将会在sys.modules中创建一个以自己名称命名的和__main__模块无关的模块


2、

If you do import foo inside bar and import bar inside foo, it will work fine. By the time anything actually runs, both modules will be fully loaded and will have references to each other.

The problem is when instead you do from foo import abc and from bar import xyz. Because now each module requires the other module to already be compiled (so that the name we are importing exists) before it can be compiled.

当你在bar模块中执行import foo,同时在foo模块中执行import bar,执行不会出现问题,直到两个模块中需要依赖彼此的程序执行时,两个模块才会完全加载,而且拥有指向彼此的引用

当你在bar模块中执行from foo import abc,同时在foo模块中执行from bar import xyz,问题出现了。因为这种情况下两个模块互相之间需要另一个模块完全编译完成,只有这样,我们才能从另外一个模块中找到我们需要import的名称,但是很明显这个时候我们的程序很难完全编译完成,除非我们至少其中的一个from ... import ...出现在最后一行

### Python 引入模块的位置规范 在 Python 中引入模块时,遵循一定的位置和顺序规范可以提高代码的可读性和维护性,并减少潜在错误的发生。以下是关于 Python 引入模块的一些重要指导原则: #### 1. **统一放在文件顶部** 通常情况下,所有的 `import` 声明应集中放置在文件的最上方[^3]。这样可以让其他开发者快速了解当前脚本依赖哪些外部库或自定义模块。 ```python # 正确做法 import os import sys def main(): pass if __name__ == "__main__": main() ``` 这种结构有助于保持代码整洁并便于调试。 --- #### 2. **分组排列** 为了增强清晰度,建议按照以下层次对导入语句进行分类和排序: - 首先导入标准库模块; - 接着导入第三方库; - 最后导入本地应用/项目特定组件。 每组之间留有一个空白行作为间隔[^4]。 ```python # 标准库 import os import sys # 第三方库 import requests from flask import Flask # 自定义模块 from my_module import MyClass ``` 这种方式不仅美观而且实用,在大型项目尤其有价值。 --- #### 3. **避免循环导入** 如果存在可能引发循环导入风险的情况,则需谨慎处理。一种解决方案是在函数内部执行局部导入而非全局范围内的提前加载[^2]。 ```python # 可能引起循环导入的例子 from module_b import BClass # 如果module_b也尝试从这里导入某些东西就麻烦了! class AClass: def interact_with_b(self): instance_of_b = BClass() # 改良版:延迟到实际需要的时候再做导入动作 class AClass: def interact_with_b(self): from module_b import BClass instance_of_b = BClass() ``` 通过这种方法能够有效规避因过早绑定而导致的问题。 --- #### 4. **动态条件下的按需加载** 对于那些仅会在特殊条件下才使用的功能或者非常耗资源的大规模框架初始化过程来说,考虑采用懒加载策略——即只在第一次需要用到该特性时才去真正完成它的实例化工作流程[^5]。 ```python some_library_instance = None def get_some_library(): global some_library_instance if some_library_instance is None: import heavyweight_lib some_library_instance = heavyweight_lib.initialize() return some_library_instance ``` 此技术特别适合于优化启动时间敏感的应用场景下性能表现。 --- ### 总结 合理安排Python中的模块导入不仅可以提升程序稳定性还能让整体架构更加优雅易懂。记住始终把所有必要的imports置于文档开头处;依据类别整理好先后次序关系;警惕可能出现的互斥引用陷阱并通过调整时机来化解它们的影响最后视具体情况灵活运用即时计算机制达成目的即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值