最近公司出了一个bug,我有点不太懂python循环import会发生什么,也不知道python是怎么处理的,周末顺着文档缕了一遍python的导入系统。
官方导入系统的文档:https://docs.python.org/zh-cn/3.12/reference/import.html
本文主要是记录一些细节、自己的理解以及一些注解。
基本介绍
import
import: import语句包含了两个操作:
- 它先搜索指定的模块,如果不存在会创建改模块;
- 然后将搜索结果绑定到当前作用域中;
展开来说,就是import首先会调用__import__将模块拿到引用,然后import会处理名字绑定相关的操作,类似于
import math # 将math模块绑定到locals()的math
import abc as a # 将abc模块绑定到locals()的a
from abc import d # 将abc.a绑定到locals()的d上
__import__
看完前面的,__import__就很清晰了,它在设计上是仅用来做搜索以及在没搜到时创建模块的功能的,但是虽然不会影响globals()和locals(),但是会修改sys.modules。
模块和包
模块:Python只有一种模块对象类型,所有模块都属于该类型,无论是从python还是C++实现的类模块。
包:可以将模块理解成文件,包是目录,包可以拥有子包,也可以拥有模块。
包又分为常规包和命名空间包,常规包就是目录有__init__.py的包,命名空间包就可以让一个包内的模块不在同一个目录,甚至
注:
- 包是为了帮助组织模块并提供名称层次结构,Python 还引入的概念。
- 所有的包也都是模块
- 拥有__path__的模块会被当做是包
加载模块
[原文]
当一个模块说明被找到时,导入机制将在加载该模块时使用它(及其所包含的加载器)。 下面是导入的加载部分所发生过程的简要说明:
module = None
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
# It is assumed 'exec_module' will also be defined on the loader.
module = spec.loader.create_module(spec)
if module is None:
module = ModuleType(spec.name)
# The import-related module attributes get set here:
_init_module_attrs(spec, module)
if spec.loader is None:
# unsupported
raise ImportError
if spec.origin is None and spec.submodule_search_locations is not None:
# namespace package
sys.modules[spec.name] = module
elif not hasattr(spec.loader, 'exec_module'):
module = spec.loader.load_module(spec.name)
else:
sys.modules[spec.name] = module
try:
spec.loader.exec_module(module)
except BaseException:
try:
del sys.modules[spec.name]
except KeyError:
pass
raise
return sys.modules[spec.name]
循环import的问题
主要文章: https://www.jb51.net/article/51815.htm
# 文件A.py:
from B import D
class C: pass
# 文件B.py
from A import C
class D: pass
这个过程会直接报错,但是如果将from A import C改成import A就不会报错,这是为什么呢?
可以看到前面的文档里的内容,包括这段开头的文章。
在import时首先查看是否存在这个模块,如果存在,则直接获取,如果不存在,则先创建一个空的占用,然后再执行loader.execute()
两个连续的from xx import xx发生的事情就是,在第二次执行from A import C时,会发现sys.modules里面已经有A,那么import做的事情是获取A.C,然后把引用放进globals。
但是这个A是个空的模块,所以就Trace了。
End