关于 python 的 __init__.py 最清楚的介绍

在写 python package 的时候,不可避免的和 init.py 打交道,查了很多资料,发觉下面的文章是介绍的最清楚的,来源 老王的python ,我重新整理了一下格式:

python 中的 Module 是比较重要的概念。常见的情况是,事先写好一个.py 文件,在另一个文件中需要 import 时,将事先写好的 .py 文件拷贝 到当前目录,或者是在 sys.path 中增加事先写好的 .py 文件所在的目录,然后 import。这样的做法,对于少数文件是可行的,但如果程序数目很多,层级很复杂,就很吃力了。

有没有办法,像 Java 的 Package 一样,将多个 .py 文件组织起来,以便在外部统一调用,和在内部互相调用呢?答案是有的。

主要是用到 python 的包的概念,python init.py 在包里起一个比较重要的作用。

要弄明白这个问题,首先要知道,python 在执行 import 语句时,到底进行了什么操作,按照 python 的文档,它执行了如下操作:

第1步,创建一个新的,空的 module 对象(它可能包含多个 module );
第2步,把这个 module 对象插入 sys.module 中
第3步,装载 module 的代码(如果需要,首先必须编译)
第4步,执行新的 module 中对应的代码。

在执行第3步时,首先要找到 module 程序所在的位置,其原理为:

如果需要导入的 module 的名字是 m1,则解释器必须找到 m1.py,它首先在当前目录查找,然后是在环境变量 PYTHONPATH 中查找。PYTHONPATH 可以视为系统的 PATH 变量一类的东西,其中包含若干个目录。如果 PYTHONPATH 没有设定,或者找不到 m1.py,则继续搜索 与 python 的安装设置相关的默认路径,在 Unix下,通常是 /usr/local/lib/python。

事实上,搜索的顺序是:当前路径 (以及从当前目录指定的 sys.path),然后是 PYTHONPATH,然后是 python的安装设置相关的默认路径。正因为存在这样的顺序,如果当前 路径或 PYTHONPATH 中存在与标准 module 同样的 module,则会覆盖标准 module。也就是说,如果当前目录下存在 xml.py,那么执行 import xml 时,导入的是当前目录下的 module,而不是系统标准的xml。

了解了这些,我们就可以先构建一个 package,以普通 module 的方式导入,就可以直接访问此 package 中的各个module 了。

Python 中的 package 定义很简单,其层次结构与程序所在目录的层次结构相同,这一点与 Java 类似,唯一不同的地方在于,python 中的 package 必须包含一个 init.py 的文件。
例如,我们可以这样组织一个 package:

package1/
init.py
subPack1/
init.py
module_11.py
module_12.py
module_13.py
subPack2/
init.py
module_21.py
module_22.py
……

init.py 可以为空,只要它存在,就表明此目录应被作为一个 package 处理。当然,init.py 中也可以设置相应的内容,下文详细介绍。

好了,现在我们在module_11.py中定义一个函数:

def funA():
    print "funcA in module_11"
    return

 

在顶层目录(也就是 package1 所在的目录,当然也参考上面的介绍,将 package1 放在解释器能够搜索到的地方)运行python:

>>>from package1.subPack1.module_11 import funcA
>>>funcA()
funcA in module_11

 

这样,我们就按照package的层次关系,正确调用了module_11中的函数。

细心的用户会发现,有时在 import 语句中会出现通配符 *,导入某个 module 中的所有元素,这是怎么实现的呢?
答案就在 init.py 中。我们在 ubPack1 的 init.py 文件中写

__all__ = ['module_13', 'module_12']

 

然后进入python

>>>from package1.subPack1 import *
>>>module_11.funcA()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named module_11

 

也就是说,以 * 导入时,package 内的 module 是受 init.py 限制的。

好了,最后来看看,如何在 package 内部互相调用。
如果希望调用同一个 package 中的 module,则直接 import 即可。也就是说,在 module_12.py 中,可以直接使用

import module_11

 

如果不在同一个 package 中,例如我们希望在 module_21.py 中调用 module_11.py 中的 FuncA,则应该这样:

from module_11 import funcA

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注