python爬虫
直播中

mushenmu

3年用户 732经验值
擅长:可编程逻辑
私信 关注
[经验]

python常规包与命名空间包

python常规包与命名空间包1. 常规包在 Python 3.3 之前或者说 Python 2 中,一个包想要被导入使用,那么该包内必须要有 __init__.py 文件,这个文件是 Python 识别一个文件夹是否是一个 Python 的重要标志。
举个例子,现在有如下的目录树,demo 及子文件夹 foo 和 bar 下都有 __init__.py 文件。
  1. $ tree demo/
  2. demo/
  3. ├── bar
  4. │   └── __init__.py
  5. ├── foo
  6. │   └── __init__.py
  7. └── __init__.py
在该目录下进入 Python Console 模式,然后就可以正常导入了
  1. >>> import demo
  2. >>> import demo.bar
  3. >>> import demo.foo
如果此时我把 demo 目录下的 __init__.py 删除
  1. $ tree demo/
  2. demo/
  3. ├── bar
  4. │   └── __init__.py
  5. └── foo
  6.     └── __init__.py
再导入就会报错。
  1. >>> import demo
  2. Traceback (most recent call last):
  3.   File "", line 1, in
  4. ImportError: No module named demo
2. 命名空间包在 Python 3.3 之后(PEP 420),即使一个文件夹中没有定义 __init__.py,也是可以被导入的,只不过它不是以 Python 包的形式导入,而是以命名空间包 (Namespace package) 的形式被导入,而这一特性是在 Python 3.3 被引入的。
比如还是上面的目录结构:
  1. $ tree demo/
  2. demo/
  3. ├── bar
  4. │   └── __init__.py
  5. └── foo
  6.     └── __init__.py
在 Python 3 下进入 Python Console 模式,发现导入是正常的
  1. >>> import demo
  2. >>> import demo.foo
  3. >>> import demo.bar
使用 __path__ 查看一下,发现 demo 不再是一个常规包了,而是一个 namespace package
  1. >>> demo

  2. >>>
  3. >>> demo.__path__
  4. _NamespacePath(['/root/python/demo'])
3. 空间命名包的好处利用命名空间包这个技术,可以用来导入目录分散的代码。
比如有如下的目录树
  1. $ tree
  2. .
  3. ├── xc-pkg
  4. │   └── demo
  5. │       └── foo
  6. │           └── __init__.py
  7. └── xm-pkg
  8.     └── demo
  9.         └── bar
  10.             └── __init__.py
在这 xc-pkg 和 xm-pkg 这两个目录里,都有着共同的命名空间 demo。这时候再导入这两个包的时候,发现这两个包被合并到一起了
  1. >>> import sys
  2. >>> sys.path.extend(['xm-pkg', 'xc-pkg'])
  3. >>>
  4. >>> import demo.foo
  5. >>> import demo.bar
  6. >>> demo
在这里工作的机制被称为命名空间包的一个特征。从本质上讲,命名空间包是一种特殊的封装设计,为合并不同的目录的代码到一个共同的命名空间。
命名空间包的关键是确保顶级目录中没有 __init__.py 文件来作为共同的命名空间。缺失 __init__.py 文件使得在导入包的时候会发生有趣的事情:这并没有产生错误,解释器创建了一个由所有包含匹配包名的目录组成的列表。特殊的包命名空间模块被创建,只读的目录列表副本被存储在其 __path__ 变量中。
  1. >>> demo.__path__
  2. _NamespacePath(['xm-pkg/demo', 'xc-pkg/demo'])
一个包是否被作为一个包命名空间的主要方法是检查其 __file__ 属性。如果没有,那包是个命名空间。这也可以由其字符表现形式中的 namespace 这个词体现出来。
  1. >>> demo

  2. >>>
  3. >>> demo.__file__
  4. Traceback (most recent call last):
  5.   File "", line 1, in
  6. AttributeError: module 'demo' has no attribute '__file__'

更多回帖

发帖
×
20
完善资料,
赚取积分