一般项目中模块一般会分布在文件系统的不同路径中。Python解释器找到某个模块并将其导入命名空间会遵循一定的搜索规则,这个规则就是模块路径搜索顺序。
模块路径搜索
python搜索模块的路径顺序:
1)、程序运行的工作目录
2)、PTYHONPATH目录(如果已经进行了设置)
3)、标准连接库目录(linux下一般在/usr/local/lib/python2.X/)
4)、任何的.pth文件的内容(如果存在的话).新功能,允许用户把有效果的目录添加到模块搜索路径中去,.pth后缀的文本文件中一行一行的地列出目录。
这四个组合起来就变成了sys.path了。sys.path是python的搜索模块的路径集,是一个list,如
>>> importsys
>>>sys.path
['','/usr/lib64/python27.zip', '/usr/lib64/python2.7','/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk','/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload','/root/.local/lib/python2.7/site-packages', '/usr/lib64/python2.7/site-packages','/usr/lib64/python2.7/site-packages/gtk-2.0','/usr/lib/python2.7/site-packages']
>>>
因此只要模块或者包所在的目录在sys.path中,就可以使用import 模块或import 包命令格式来使用。下面以如下一个文件结构为例
[root@localhostpython]# tree
.
├──moduleA.py
├──mylib
│ ├──moduleB_2.py
│ ├──moduleB.py
│ └──sublib
│ ├──moduleC.py
├──otherlib
│ ├──moduleD.py
└──run.py
同级目录导入
处于同级目录的两个模块直接使用import 或from 模块名 import *相互调用。但这也只限于其中一个模块文件作为主程序运行。如在run.py中下列导入是合法的导入
import moduleA
而无法直接导入其他目录中的模块,如import moduleB是非法的。而且指定全路径也是极其错误的,如import /test/python/mylib/moduleB,这和php不一样。
而且导入过程和python解释器当前工作目录无关,
[root@localhostpython]# python run.py
in moduleA file
in run file
[root@localhostpython]# cd ..
[root@localhosttest]# python python/run.py
in moduleA file
in run file
跨目录调用
跨目录调用分以下几种情况
[if !supportLists]• [endif]主程序所在的目录是模块所在目录的父(或祖辈)目录
[if !supportLists]• [endif]主程序导入上层目录中的模块
跨目录导入有统一的导入模式,可以使用sys.path.append方法将模块加入到程序搜索路径中。如在run.py中导入moduleB.py,我们可以这么做
[root@localhostpython]# vi run.py
import sys
sys.path.append('/test/python/mylib')
import moduleB
print("in runfile")
再次运行
[root@localhostpython]# python run.py
in moduleB file
in run file
使用这种方法有两个缺点,一是在退出python环境后自己添加的路径就会自动消失了,且它是在实践中极为脆弱,应尽量避免使用;二是它将目录名硬编码到了你的源代码。如果你的代码被移到一个新的位置,这会导致维护问题。
为了解决这些问题,可以有以下两个方法。
使用PYTHONPATH环境变量来添加。在自定义应用程序中,这样的环境变量可在程序启动时设置或通过shell 脚本。如shell中,
[root@localhost python]# export PYTHONPATH=/usr/lib/
[root@localhost python]# python
Python 2.7.5 (default, Apr 92019, 14:30:50)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux2
Type "help", "copyright", "credits" or"license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib','/usr/lib64/python27.zip', '/usr/lib64/python2.7','/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk','/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload','/root/.local/lib/python2.7/site-packages','/usr/lib64/python2.7/site-packages','/usr/lib64/python2.7/site-packages/gtk-2.0','/usr/lib/python2.7/site-packages']
>>>
上述操作只针对当前窗口生效,我们可以vi /etc/profile,在最后添加 export PYTHONPATH=/usr/lib/对所有用户生效。
使用.pth文件,在site-packages 文件中创建 .pth文件,将模块的路径写进去,一行一个路径。site-packages目录是第三方包和模块安装的目录。如果你手动安装你的代码,它将被安装到site-packages目录。虽然用于配置path的.pth 文件必须放置在site-packages里,但它配置的路径可以是系统上任何你希望的目录。因此,你可以把你的代码放在一系列不同的目录,只要那些目录包含在.pth 文件里。
除了上述方法外,还可以将模块封装成包,见下节。
包
python中,每个py文件被称之为模块,而每个包含有__init__.py文件的目录或文件夹被称为包。在创建许许多多模块后,我们可能希望将某些功能相近的文件组织在同一文件夹下,在这个文件夹下再增加一个 __init__.py文件,这个文件夹就成为了包。即包是python模块文件所在的目录,且该目录下必须存在__init__.py文件。典型的包结构如下:
package
├── __init__.py
├── module_a.py
└── module_b.py
最简单的情况下,只需要一个空的 __init__.py 文件即可。当然它也可以执行包的初始化代码或者设置__all__值。包下面是一些模块文件和子目录,假如子目录中也有 __init__.py 那么它就是这个包的子包了。
包的使用同模块一样,使用 import 、 from ... import 语句,来改变命名空间。
__init__.py
如上所述,封装成包是很简单的。在文件系统上组织你的代码,并确保每个目录都定义了一个__init__.py文件。
如在mylib文件夹下新建一个__init__文件。
[root@localhost mylib]# touch __init__.py
那么mylib目录现在就是一个包。如果要在run.py中导入moduleB.py模块,可以写入如下格式导入moduleB.py
[root@localhost python]# vi run.py
import mylib.moduleB
….
[root@localhost python]# python run.py
in moduleB file
in run file
即导入包中的模块使用如下格式
pkg1.pkg2.~.pkgn.模块名
包的导入注意点凡是在导入时带点的,点的左边都必须是一个包,否则非法。如果想要导入moduleC.py模块,还需要在sublib目录下新建__init__.py文件。如
[root@localhost sublib]# touch __init__.py
[root@localhost python]#vi run.py
import mylib.sublib.moduleC
…
[root@localhost python]# python run.py
in moduleC file
in run file
上述导入指明了包的全路径,这个路径(顶层包名)是相对于主程序所在目录而言的,
[root@localhost mylib]# vi moduleB.py
import otherlib.moduleD #导入otherlib包
import moduleB_2 #同目录下的模块
print("in moduleB file")
[root@localhost mylib]# python moduleB.py
Traceback (most recent call last):
File "moduleB.py",line 1, in
import otherlib.moduleD
ImportError: No module named otherlib.moduleD
这是因为moduleB.py所在的mylib目录下没有otherlib包。
这也意味着我们在导入模块的时候,最好写全包的路径(从顶层包开始),除非两个模块在同一个目录,如
[root@localhost python]# vi run.py
import mylib.moduleB #导入mylib包
print("in run file")
[root@localhost python]# python run.py
in moduleD file
in moduleB_2 file
in moduleB file
in run file
它的导入过程是解释器首先发现要导入mylib包下的moduleB.py,于是在该包下找到了moduleB.py,在导入该文件时,又发现需要导入otherlib包下的moduleD.py,于是去otherlib(就在当前主程序同目录下)包下找moduleD.py。
以上的用法都是使用了包的全路径来导入的。这实际上算是一种绝对路径。使用绝对路径名的不利之处是这将顶层包名硬编码到你的源码中。如果你想重新组织它,你的代码将更脆,很难工作。举个例子,如果你改变了包名,你就必须检查所有文件来修正源码。同样,硬编码的名称会使移动代码变得困难。
而相对路径中,用. 为当前目录,.. 为父目录。但这种方法只适用于from … import的导入方式。如
[root@localhost python]# vi run.py
import mylib.sublib.moduleC
print("in run file")
[root@localhost sublib]# cat moduleC.py
from .. import moduleB_2
#import mylib.moduleB_2
print("in moduleC file")
上述语句即实现在sublib/module.py中导入上层目录中的moduleB_2.py。
[root@localhost python]# python run.py
in moduleB_2 file
in moduleC file
in run file
注意Relative imports use a module’s name attribute to
determine that module’s position in the package hierarchy. Note that relative imports are based on the name of the current module.
上述导入的过程是通过import mylib.sublib.moduleC找到moduleC.py文件,在该文件中,因为有相对导入语句,就以当前模块为参考,去找父目录下找moduleB_2.py,它实际上等价于import
mylib.moduleB_2。
top-levelscript
每一个被直接运行的python
script都会被视作是top-level script。top-level
script的__name__被自动设置成__main__。那么它所在的目录将不被python视为包,通过相对导入将不起作用,如
[root@localhost mylib]# vi moduleB.py
from . import moduleB_2
print("in moduleB file")
[root@localhost mylib]# python moduleB.py
Traceback (most recent call last):
File "moduleB.py",line 3, in
from . import moduleB_2
ValueError: Attempted relative import in non-package
moduleB.py所在的目录现在不是包了,通过相对导入将不起作用。
再比如
[root@localhost mylib]# vi moduleB.py
import sublib.moduleC
print("in moduleB file")
[root@localhost mylib]# cat sublib/moduleC.py
from .. import moduleB_2
print("in moduleC file")
[root@localhost mylib]# python moduleB.py
Traceback (most recent call last):
File "moduleB.py",line 1, in
import sublib.moduleC
File"/test/python/mylib/sublib/moduleC.py", line 1, in
from .. import moduleB_2
ValueError: Attempted relative import beyond toplevel package
这是因为moduleB.py作为主程序入口,mylib目录就不是包了,而sublib就变成了顶层包。在sublib/moduleC.py中使用相对导入使用父目录下的模块当然就不会被允许了。