之前写脚本基本上都是在一个目录中完成,导入方便, 短平快,现在自己写稍微大一点的程序,就栽在模块导入上,学艺不精,重新看《学习手册》和实践,做一次总结。
模块
模块是为了方便重用,通常都是放在一个目录下面,调用方便,因为所有文 件都在顶层,不存在路径问题。
- import 获取一个模块的整体
- from 获取一个模块的属性
# mod_4.py
def mod4():
print("in mode_4")
mod_4 = 4
- mod_4_1.py 调用 mod_4 模块,模块必需以py结尾,导入时文件名去掉后缀
# mod_4_1.py
import mod_4
from mod_4 import mod4
def mod41():
print("in mod41")
mod_4.mod4() # import 导入整个模块
mod41 = 41
mod41()
mod4() # from 导入模块属性
--------------------------------------------
terry@terry:/test_dir0$ python3 dir1/dir3/dir4/mod_4_1.py
in mod41
in mode_4
in mode_4
模块递归导入
模块递归导入是多个文件互相导入的情况,虽然在引用属性时有点,但与包导入的意义是不一样的
# mod_4_2.py
import mod_4_1
def mod42():
mod_4_1.mod_4.mod4() # 这边的点号没有目录结构,仅仅是属性引用
mod42()
----------------------------------------------------
terry@terry:/test_dir0$ python3 dir1/dir3/dir4/mod_4_2.py
in mod41 # 多余的输出
in mode_4 # 多余的输出
in mode_4 # 多余的输出
in mode_4
name == 'main'
这是一个讨巧的方法,当我们调用模块时我们只关心我们所需要的接口数据,而不需要一些多余的输出,如果要测试模块本身,我们又希望模块能直接运行,因此当执行为脚本时,name__会变成__main,此时模块当脚本来执行,当被模块调用时 __name__是模块名。
修改 # mod_4_1.py 减少不必要的输出,测试文件名
import mod_4
from mod_4 import mod4
def mod41():
print("in mod41")
mod_4.mod4()
mod_41 = 41
print('file name is %s' % (__name__)) # 测试文件名
if __name__ == '__main__':
mod41() # 调用放在执行文件中,模块调用时不执行
mod4() # 调用放在执行文件中,模块调用时不执行
--------------------------------------------------------
terry@terry:/test_dir0$ python3 dir1/dir3/dir4/mod_4_2.py
file name is mod_4_1 # 当模块调用时 file name 是 'mod_4_1'
in mode_4 # 没有多余的输出,只有接口输出
---------------------------------------------------
terry@terry:/test_dir0$ python3 dir1/dir3/dir4/mod_4_1.py
file name is __main__ # 当执行文 件时,file name是 '__main__'
in mod41 # 调用 file_name = main下面的程序
in mode_4
in mode_4
包导入
包导入是相对于包来说,在python2中包必需要有__init__.py, 在python3.4后没有这个要求
- 绝对导入
使用路径语法,由顶层目录一直往下
- 相对导入
包内导入相对于包,而不是完整路径
- python2
默认search路径为包内
- python3
默认为包外 sys.path, 如果包内search 需要 from . 显示定义
包结构
terry@terry:/test_dir0$ tree -L 5
.
├── dir1 # 第一 子层
│ ├── dir2 # 第二子层
│ │ └── mod_2.py
│ └── dir3 # 第二子层
│ ├── dir4 # 第三子层
│ │ ├── mod_4_1.py
│ │ ├── mod_4_2.py
│ │ └── mod_4.py
│ └── mod_3.py
└── top.py # 顶层
顶层文件导入子模块
这是包导入的核心,一般应用程序会有一个主程序入口,其它模块为主程序调用,主程序处在包的顶层,如 top.py
# top.py
from dir1.dir3.dir4 import mod_4
from dir1.dir3.dir4.mod_4_1 import mod41
print(mod_4.mod_4)
mod41()
----------------------------------------------------
terry@terry:/test_dir0$ python3 top.py
Traceback (most recent call last):
File "top.py", line 7, in <module>
from dir1.dir3.dir4.mod_4_1 import mod41
File "/test_dir0/dir1/dir3/dir4/mod_4_1.py", line 6, in <module>
import mod_4
ImportError: No module named 'mod_4'
这个错误提示没有mod_4模块,因为当执行 dir1/dir3/dir4/mod_4_1.py可能找到同级目录下的mod_4.py,而现在执行脚本的目录在 test_dir0/, 该目录下没有 mod_4.py
# mod_4_1.py
import dir1.dir3.dir4.mod_4 # import 完整路径
from dir1.dir3.dir4.mod_4 import mod4 # from 完整绝对路径
def mod41():
print("in mod41")
dir1.dir3.dir4.mod_4.mod4() # 包导入时用import是不明智的
mod_41 = 41
print('file name is %s' % (__name__)) # 测试文件名
if __name__ == '__main__':
mod41()
mod4()
----------------------------------------------------------
terry@terry:/test_dir0$ python3 top.py
file name is dir1.dir3.dir4.mod_4_1
4
in mod41
in mode_4
当执行顶层文件,子包导入时需标明从顶层文件开始寻找的目录路径,否则找不到模块
相对导入方式
# mod_4_1.py
from .mod_4 import mod4 # 相对导入只支持 from . 的显示定义
def mod41():
print("in mod41")
mod4() # 包导入时用import是不明智的
mod_41 = 41
print('file name is %s' % (__name__)) # 测试文件名
if __name__ == '__main__':
mod41()
mod4()
----------------------------------------------------------
terry@terry:/test_dir0$ python3 top.py
file name is dir1.dir3.dir4.mod_4_1
4
in mod41
in mode_4
可见相对导入的方式比绝对导入写起来要方便,绝对导入以顶层文件作为基础 dir1.dir3.dir4.mod_4 相对导入以现在文件所在目录作为参考导入同级文 件 from .mod import mod4, 上级为 from ..dir4.mod4_4 import mod4, 上上级目录 from ...dir2 import mod_2
import的讨巧方式
# mod_4_1.py
import dir1.dir3.dir4.mod_4 as md # 相对导入只支持 from . 的显示定义
def mod41():
print("in mod41")
md.mod4() # 用 as 可以替换 dir1.dir3.dir4.mod_4.mod4()
mod_41 = 41
print('file name is %s' % (__name__)) # 测试文件名
if __name__ == '__main__':
mod41()
mod4()
----------------------------------------------------------
terry@terry:/test_dir0$ python3 top.py
file name is dir1.dir3.dir4.mod_4_1
4
in mod41
in mode_4
执行包模块
terry@terry:/test_dir0$ python3 dir1/dir3/dir4/mod_4_1.py
Traceback (most recent call last):
File "dir1/dir3/dir4/mod_4_1.py", line 8, in <module>
import dir1.dir3.dir4.mod_4 as md
ImportError: No module named 'dir1'
- 找不到dir1是因为python3执行 dir1/dir3/dir4/mod_4_1.py,此时顶层在dir1/dir3/dir4下面,dir1已经超过顶层了
- 如果要执行 mod_4_1.py必需改成 import mod_4, 如果要让top.py能正确运行则必需 import dir1.dir3.dir4.mod_4 as md 有路径信息,有什么方法可以既让top.py正确执行,又可以执行mod_4_1.py自测呢
terry@terry:/test_dir0$ python3 -m dir1.dir3.dir4.mod_4_1
file name is __main__
in mod41
in mode_4
in mode_4
通过执行模块的方式 python3 -m dir1.dir3.dir4.mod_4_1执行,此时相对导入也可行,
# mod_4_1.py
from .mod_4 import mod4 # 相对导入只支持 from . 的显示定义
def mod41():
print("in mod41")
mod4() # 包导入时用import是不明智的
mod_41 = 41
print('file name is %s' % (__name__)) # 测试文件名
if __name__ == '__main__':
mod41()
mod4()
相对导入更加灵活,执行路径不一定要在/test_dir0/下面,也可以在/test_dir0/dir1/,/test_dir0/dir1/dir2/, /test_dir0/dir1/dir2/dir3
terry@terry:~/learn/python/module/twentythree/test_dir0/dir1/dir3$ python -m
dir4.mod_4_1
/usr/bin/python: No module named dir4
上面提示的错误是因为没有 init.py, python2不认为dir4是一个包,而提示错误, python3.5没有这个限制
在子模块中导入父级模块
terry@terry:/test_dir0$ tree -L 5
.
├── dir1 # 第一 子层
│ ├── dir2 # 第二子层
│ │ └── mod_2.py
│ └── dir3 # 第二子层
│ ├── dir4 # 第三子层
│ │ ├── mod_4_1.py
│ │ ├── mod_4_2.py
│ │ └── mod_4.py
│ └── mod_3.py
└── top.py # 顶层
- 如果要在mod_4_1.py中导入 dir2中的mod_2.py,要如何写
# mod_2.py
def mod2():
print('in mod_2')
mod_2 =2
# mod_4_1.py
from . import mod_4 as md # 同级别包,或子级别包用相对更好
from ...dir2 import mod_2 (import dir1.dir2.mod2 as mod_2)
def mod41():
print('in mod41')
md.mod4()
mod_41 = 41
if __name__ == '__main__':
mod41()
md.mod4()
print(mod_2.mod_2)
-------------------------------------------------
terry@terry:/test_dir0$ python3 -m dir1.dir3.dir4.mod_4_1
file name is __main__
in mod41
in mode_4
in mode_4
2
-------------------------------------------------
terry@terry:/test_dir0/dir1/dir3/dir4$ python3 mod_4_1.py
Traceback (most recent call last):
File "mod_4_1.py", line 4, in <module>
from ..dir4 import mod_4 as md
SystemError: Parent module '' not loaded, cannot perform relative import
包模块不允许直接运行
总结
- 包导入相对于模块具有目录路径信息,导入时使用相对导入较好,一方面执行路径可选择, 另一方面避免硬编码,一旦顶层目录更名,所有模块都要改写
- 无论是包绝对导入还是包相对导入, 都不能直接运行, 需要通过 python3 -m A.B.C方式运行
- 绝对导入确定了顶层,并需要写完整路径,相对导入则较为灵活
- python2要注意每个包必需有__init__.py,
- 包导入在执行模块时注意路径,相对导入不能超过顶层包attempted relative import beyond top-level package,说明执行的目录不够,还需要 cd ..
参考
- 《Python 学习手册》第四版
- https://segmentfault.com/q/1010000012365228 pep420
- https://www.cnblogs.com/soukingang/p/5517097.html python2/3不同