Python 的 import 机制
1.1 相对/绝对导入
当我们 import 导入模块或包时,Python 提供两种导入方式:
- 相对导入(relative import ):from . import B 或 from ..A import B,其中.表示当前模块,..表示上层模块
- 绝对导入(absolute import):import foo.bar 或者 form foo import bar
你可以根据实际需要进行选择,但有必要说明的是,在早期的版本( Python2.6 之前),Python 默认使用的相对导入。而后来的版本中( Python2.6 之后),都以绝对导入为默认使用的导入方式。
使用绝对路径和相对路径各有利弊:
- 当你在开发维护自己的项目时,应当使用相对路径导入,这样可以避免硬编码带来的麻烦。
- 而使用绝对路径,会让你模块导入结构更加清晰,而且也避免了重名的包冲突而导入错误。
1.2导入的标准写法
在 PEP8 中对模块的导入提出了要求,遵守 PEP8规范能让你的代码更具有可读性,我这边也列一下:
- import 语句应当分行书写
# bad
import os,sys
# good
import os
import sys
复制代码
- import语句应当使用absolute import
# bad
from ..bar import Bar
# good
from foo.bar import test
复制代码
-
import语句应当放在文件头部,置于模块说明及docstring之后,全局变量之前
-
import语句应该按照顺序排列,每组之间用一个空格分隔,按照内置模块,第三方模块,自己所写的模块调用顺序,同时每组内部按照字母表顺序排列
关于python3和python2 import区别
解释1:
python2是默认相对路径导入,python3是默认绝对路径导入
首先这个包的导入机制,就是你在一个module里面引用另一个module,python运行文件有两种方式,一种是直接以主文件运行(默认以这种方式运行,同下面一种有点区别),一种是以module形式运行,就是用python -m filename
方式调用.
以module的方式运行
####### 什么是相对导入和绝对导入呢
相对导入是用一个.
来声明的,相当于Unix上的选择当前文件夹.
假设你的文件目录为下面的
main
| main.py
| __init__py
| momod+
| | __init__.py
| | pack.py
| | flask
| | | __init__.py
| | | myflask.py
--------------------
python里面的module分三种,一种是build-in module(内建库),一种是第三方库,还有一种是你自己写的库(如上面的flask).
在python2里面,当你import 一个module时,搜索的顺序是 内建库,自己的库,第三方库.
####### 而在python3里面顺序为 内建库,第三方库,你自己的库.
比如在momod 里面的pack.py假如想引用flask的myflask.py要这样写
from .flask import myflask
########### 假如你在py2中写了 from flask import myflask
(并且你安装了flask库),这个是可以成功运行但是在py3中就会报错,因为他会优先导入flask库假如你没有显示声明相对导入的话.
上面成立的前提是将pack.py以module方式运行,或者运行main.py在其中引入pack.py.接下来讲讲以主文件运行的不同.
当你直接使用
python pack.py
你假如在pack.py里面使用了这个
from .flask import myflask
引用了自己的myflask module,在py2和py3中都会下面报这个错
SystemError: Parent module '' not loaded, cannot perform relative import
因为当你以主文件方式运行 pack.py ,python会吧pack.py重命名为__main__
,所以用.相对路径也不会是当前文件.所以全部都只能用绝对引用.
所以在主文件运行在python3里面有个问题,假如你自己的库与第三方库有重名?
python3默认绝对路径,自己的库不会优先于第三方库被扫描.有两个解决方法,把自己的库重命名,第二个方法就是把包含主文件的文件夹加上init.py,你可以在sys.path的路径里加上..
或者具体上一个上一个文件夹的路径.怪绕口的,其实你只要python能找到你的上一个文件夹,就行.
说到这里顺便插一句对doctest和集成测试的理解.
由于我平时喜欢一边写代码一遍测试功能,图方便就使用doctest直接插在方法里面,在代码后面加上
if __name__ == '__main__':
import doctest
doctest.tesmod()
平时写小module的时候没有问题,在将python2转python3时候,出现问题,因为我要测试这个module时,会报上面的那个错,因为我要测试他的话必须将它作为主文件.查了资料知道,其实doctest虽然在当前页面代码测试,但是对于module的话,最好采用集成测试,一是module很多,假如一个一个运行很麻烦,二是有时候module必须多个一起测试,所以测试module时要用集成测试来取代doctest.
解释2:python2默认是按照相对路径导入模块和包,python3默认则是按照绝对路径导入
示例:
假设项目目录结构如下
A
|---test.py
|---A1
|---__init__.py
|---a1.py
test.py内容如下
import A1
A1.a1.foo()
a1.py内容如下
def foo():
pass
若用python2跑test.py,在A1的__init__.py中写import a1即可,这样test.py执行到import A1时会默认在A1的__init__.py的当前路径下执行__init__.py文件。
但用python3跑test.py时,如果A1的__init__.py还是写成原来那样,解释器就会报错:
ModuleNotFoundError:No module named 'a1'
1
原因就是python3是按照程序运行的绝对路径(也即test.py所在的路径)导入模块和包的。test.py执行到import A1时会默认在test.py 的路径下执行A1的 __init__.py 文件,该路径下显然没有 a1 模块。(要想成功导入,需在 __init__.py 文件中写 from . import a1。???有疑问)其中,.表示被执行的文件所在的当前路径。
在使用python3运行用python2写的一些项目代码时,需要留意这一点。