python文件间模块调用

        一般项目中模块一般会分布在文件系统的不同路径中。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中使用相对导入使用父目录下的模块当然就不会被允许了。

你可能感兴趣的:(python文件间模块调用)