python相对导入
python的相对导入就是:from .[module/package] import xxx的形式。
即加入了以.或者..等表示当前目录和上一级目录的符号,不直接指定目录
(即package,有__init__.py)名,来从对应目录导入module或从对应目录下的module导入模块内名字的方式。
具体细节
python文件中使用相对导入是和当前文件的name属性有关的。即如下的一个文件结构:
pack
|--a.py
|--b.py
|--inner
|--e.py
|--f.py
back
|--c.py
|--d.py
假如在a.py中我们有:
from .b import func
print(__name__)
运行a.py的结果是在import语句处产生了错误,当然print(__name__)
那一步不会执行,不过它的结果就是__main__。这是因为我们把a.py作为入口脚本直接执行,它的__name__为__main__不存在任何层次结构,对于相对导入而言,它的.符合就无法从当前__name__解析出有效结果。这是语法解析角度的问题,所以使用相对导入的python脚本是不可以被直接作为入口脚本执行。
那么我们如果在a.py中这样导入呢?
from back import c
print(__name__)
而c.py中如下:
from . import d
print(__name__)
如此我们没有直接运行使用相对导入的python脚本文件,并且在a.py中我们以from back import c的形式导入c,则c的__name__属性应该是back.c,进一步在c.py中使用from . import d,应该也是可行的,名字可以被解析成功。
如此尝试运行a.py,得到的结果仍然是出错,并且在a.py中就出错了,错误显示no module named back。这就牵扯到python中import模块时的寻找问题了。
sys.path
在python的sys模块中的一个方法:sys.path。它返回一个列表,该列表中的每一项都是python文件import时的查找路径。
值得注意的是:
1.sys.path在程序启动时初始化,sys.path[0]表示启用python解释器的脚本所在的目录。如果该目录不可用(比如解释器是通过交互式调用的,或脚本读取自标准输入),则sys.path[0]为空字符串,表示python优先在当前工作目录搜索模块。【这里也解释了上文为何第二次尝试失败的原因,因为back目录不在sys.path的列表中】
2.除了sys.path[0],其余的sys.path的列表项一般都是python的自带标准库与一些第三方包的路径。并且我们还可以在import其它模块前先import sys,对sys.path进行修改,增加,或删除。
3.sys.path是以入口python脚本为准的,即如有一个被import的模块tmp,假如在该模块中也import了其它模块的话,则此时是以入口脚本的sys.path为准进行寻找的,而不是该tmp模块作为入口脚本的时的sys.path。这主要是牵扯到第一点中的sys.path[0]的问题。
那么我们再做一次尝试,这里回想一下目录结构:
pack
|--a.py
|--b.py
|--inner
|--e.py
|--f.py
back
|--c.py
|--d.py
我们在a.py中如下:
from inner import e
print(__name__)
在inner下的e.py中为:
from . import f
print(__name__)
这样我们再运行a.py,其结果是:
inner.e
__main__
其中inner.e是e.py的__name__,而__main__为a.py的__name__,成功运行。即sys.path[0]为a.py所在的目录路径,而在该路径下是可以找到inner目录的,则可以顺利的从inner中导入e.py,同时e.py的__name__也是具有层次性的,语法解析没有问题。
Tip: 在e.py中我们有from . import f,但是可不可以写为from .. import b呢?答案是不可以,我们来e.py中的__name__是inner.e,所以在语法解析的时候from .就是对应的__name__中的inner,而..则在inner.e中找不到了,即__name__不是pack.inner.e,所以..对应的语法解析是会fail。
python -m
除了在python文件中直接运行脚本,也可以在命令行中以python xxx.py的形式直接运行该脚本。
那么python -m是做什么的呢?命令行下的help中写着:run library module as a script,网络上博客中的大多的说法是把python脚本作为一个模块运行,两个解释貌似还有点矛盾,其实无须理会这些说法。
python -m运行与python直接运行有两大区别:
1.python直接运行脚本是:python tmp.py的形式,如果有目录结构则是python parent/tmp.py的形式。
而python -m运行则是:python -m tmp的形式,如果有目录结构,则对应为python -m parent.tmp的形式。
2.python tmp.py对应的sys.path[0]为tmp.py所在的目录路径,就算是python parent/tmp.py也是一样。而python -m的sys.path[0]则为执行该命令所在的路径,如python -m tmp,该路径也是tmp.py文件所在路径,并无差。但是python -m parent.tmp的话,对应的路径是parent目录所在路径,而非tmp.py所在的路径。
对于以上的第二点我们再进行一下实例说明,这里再回顾一下我们的目录结构,并且给出上一级目录:
direct
|---pack
|--a.py
|--b.py
|--inner
|--e.py
|--f.py
|---back
|--c.py
|--d.py
再回顾我们上文所做的第二次尝试,即在a.py中:
from back import c
print(__name__)
在c.py中:
from . import d
print(__name__)
我们直接运行a.py或是python a.py都是出错的,因为back不在sys.path中。则我们可以退回到上一级目录direct中,并使用python -m pack.a,那么就可以顺利运行了。原因就是因为python -m的sys.path[0]并不是入口脚本所在的路径,而是命令所在目录路径。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。