Python中的namespace package

在Python 3.3之前,一个目录想被当成package被导入,必须包含__init__.py文件;而在Python 3.3及以后的版本中,__init__.py文件可以不需要,直接使用import后者from语法,就能直接导入目录,这样的目录称为namespace package。

换句话说,>=Python 3.3之后,存在两种package,一种是常规的package,即包含__init__.py的目录,一种是namespace package,即不包含__init__.py的目录。

 

搜寻规则

当引入namespace package之后,Python的搜寻规则总结如下(这里以绝对导入为例,如果是相对导入,sys.path换成当前目录即可):

1 如果按照sys.path的路径搜寻到一个常规的package,那么Python导入这个package,本次搜寻返回;

2 如果按照sys.path的路径搜寻到一个module文件,那么导入这个文件,本次搜寻返回;

3 如果按照sys.path的路径搜寻到一个目录,并且不包含__init__.py文件,那么就将这个目录记录下来(即namespace package),同时继续按照sys.path中下一个路径开始搜寻

4 如果经过1,2,3步骤都没有搜寻到指定的目录,那么,继续按照sys.path中的一个路径开始搜寻

当遍历完成所有的sys.path中的路径,同时也没有发现常规的package和module文件,那么,就按照namespace package的__path__属性包含的路径,再进行一次搜寻,如果找到,就导入,否则,导入失败。

换句话说,namespace package中__path__属性包含的路径,起到了类似sys.path的作用。

 

举例来说,加入有如下目录结构:

# 当前目录是/home/username/python
mkdir -p ns/dir1/sub
mkdir -p ns/dir2/sub

在ns/dir1/sub目录下有文件mod1.py:

# ns/dir1/sub/mod1.py
print(r'dir1\sub\mod1')

在ns/dir2/sub目录下有文件mod2.py

# ns/dir2/sub/mod2.py
print(r'dir2\sub\mod2')

为了让sys.path中包含路径/home/username/python/ns/dir1和/home/username/python/dir2,我们进行如下设置:

export PYTHONPATH=/home/username/python/ns/dir1:/home/username/python/dir2

在/home/username/python目录下进入Python交互式命令行:

>>>import sys
>>>import sys.path   #可以看到上述设置的路径生效了
['', '/home/username/python/ns/dir1', '/home/chaochao/python/ns/dir2',...]


>>>import sub    # sub被当成是一个namespace package
<module 'sub' (namespace)>

>>>sub.__path__  #__path__属性中记录了两个路径,后续的查找从这两个路径开始
_NamespacePath(['/home/chaochao/python/ns/dir1/sub', '/home/chaochao/python/ns/dir2/sub'])

>>>from sub import mod1  #成功导入mod1.py
dir1\sub\mod1

>>>from sub import mod2 #成功导入mod2.py
dir2\sub\mo2

 

需要指出的是,对于namespace package,相对导入的语法也是可以使用的,没有任何变化;同时,常规package和namespace package也是可以相互包含的,不会有任何影响

 

posted @ 2018-07-22 15:50  chaoguo1234  阅读(1433)  评论(0编辑  收藏  举报