文章目录
Python import导入问题
本文通过对绝对导入和相对导入的机理分析,进而理解Python项目中的常见导入问题, 在理解 import 之前先复习几个概念!
1.sys.modules
sys.modules是一个全局字典, 当某个模块第一次导入,sys.modules会记录该模块,并起到缓冲作用,当第二次重复导入模块的时候,python会直接在此全局字典查找,避免重复导入方法
2.__dict__属性
该属性存储对象的方法和属性,如 self.xxx
3. import 做了什么?
当python import模块的时候, 会生成一个 module对象;并且会检查模块缓存变量 sys.modules
- import moduleA
这种方式会检查 sys.modules 中是否有此moduleA模块对象,若有则不重复加载模块。 - from moduleA import B
这种方式会先 创建moduleA对象 再加载B 进moduleA的 __dict__ 属性中
4.sys.path
该属性是个列表,存放的是Python 绝对导包的路径集,是第三方模块和应用程序模块的搜索路径
总结 python的导包流程(绝对导入):
当我们在 ‘import sys’ , ‘import requests’ , 'import moduleA’时发生了什么? 这三种 分别是 标准库、第三方模块 、本地应用程序模块,这三种包含了所有导包的场景.
- Python会首先在 sys.modules 中搜索 sys 模块,若 此缓存中有该模块,则将缓存内容直接返回,导入结束;
- 若缓存中没有该模块,则Python会先搜索 自己的标准库,sys是Python的标准库,所以到此模块导入动作结束,而requests 和 moduleA 此时继续执行第三步;
- 此时会搜索 sys.path,这是Python 绝对导包的 搜索路径,列表格式,路径有优先级,索引靠前的优先级高;
- Python为 模块在本地作用域 简历 module 对象,模块导入完成
ps: Python2.4之后已经取消了隐式相对导入这个机制,延伸_ 什么是隐式相对导入?
案例说明
root_path
|---c.py
|---import_demo/
|---__init__.py
|---a.py
|---os.py
|---requests.py
a.py
import os # 此时 会导入标准库的os模块,不会导入同级目录下的 os模块, 而在python2.4会导入 同级目录下的os
import requests # 此时导入
绝对导入
# 绝对导入的两种方式
import moduleA
from moduleA import *
1.此时若 moduleA 为标准库的
相对导入
# 相对导入只能通过 from xx import xx 的方式
from . import B # 从当前目录导入B
from .. import B # 从上级目录导入B
导包场景测试
环境 Python3.7, 2.7
root_path
|---import_demo/
|---__init__.py
|---a.py
|---os.py
|---requests.py
|---inner/
|---__init__.py
|---inner_test.py
a.py
import os
import requests
from inner_package import *
os.py
print('This is os module')
requests.py
print('This is requests module')
inner/init.py
# 第一种
import test
# 第二种
from test import *
# 第三种
from . test import *
inner/inner_test.py
print('This is test module')
说明☆
Python 2.7 和3.7 执行情况说明
a是执行文件,即import_demo为顶层目录,此时在a.py中不允许相对导入存在,两种导包方式: import xx / from xx import xx 都为绝对导入形式,此时除了Python的标准库外,其余包会严格按照 sys.path这一 PYPATHON 环境变量映射 顺序 来执行,此时的现象是:
- 因为 os为标准库,requests 为 第三方库 所以此时 os会执行标准库导入,而requests会执行 sys.path顺序导入,在这个列表中 当前目录的优先级最高,所以会覆盖掉 第三方库的模块;
- 在 inner 包的 init.py 中执行三种导包方式 都可以导入 test模块,因为 2.7版本 默认是 执行相对导入的,而在 Python3.7 版本执行 第一种和第二种方式会报错,只有宝贝执行第三种相对导入才会正常执行,因为 python3.7 默认执行 绝对导入,即搜索sys.path列表中的路径
总结:
-
Python2.x默认执行 相对导入 Python3.x 默认执行绝对导入;
-
相对导入的概念 只在包内导入有效,顶层脚本执行如果有相对导入格式的代码会报错,顶层脚本的非标准库模块导入严格执行 sys.path
重点之-相对导入
相对导入与 模块属性 __name__有关
如下目录结构mypackage/ __init__.py A/ __init__.py a.py B/ __init__.py b.py run.py main.py
若 以 run.py 作为启动文件,则此时有
- run.py 的 __name__ 为 __main__
- a.py 的 __name__ 为 A.a
此时A成为顶层的包,所以相对导入最多只能访问到A,A之外的层次结构是不可见的
若以 main.py 作为启动文件,则此时有
- main.py 的 __name__ 为 __main__
- a.py 的 __name__ 为mypackage.A.a
mypackage成为顶层包,相对导入作用域扩大,B/包对a.py可见
即: 相对导入只适用于包中的模块,顶层的模块中将不起作用
-
在Python2.x版本中,若要在包内进行导入,可以用 非相对导入的形式进行相对导入。如 import xx 或者 from xx import xx,而非必须 from . import xx 这种形式
-
python 2.x和python 3.x的导包机制
-
顶层脚本导入 与标准库同名的os模块,此时会执行标准库的导入,而非当前目录的os模块,而包内导入 与标准库同名的os模块,则会以当前目录的os模块为准,即有如下目录结构
root_path
|---import_demo/
|---__init__.py
|---a.py
|---os.py
|---requests.py
|---inner/
|---__init__.py
|---inner_test.py
|---os.py
a.py
import inner
inner/init.py
import os
inner/os.py
print('This is my os module')
执行 a.py 结果 :(注:此只在 2.x版本有效 3.x版本取消了 这种相对导包方式)
- Python2.x与Python3.x导包方面只在 包内导包默认方式上的区别,暂未遇到其他
最后强调
Python2.x版本会在 2020.1.1 停止更新,所以后续的更新会以3.x版本为准,新入手的 pythoner 请以3.x版本为准进行学习!