[英]relative position and content of namespace packages in Python >= 3.3
我阅读了文档和相当多的 stackoverflow 帖子,但没有找到明确的答案来解决我的疑问。
我想我了解命名空间包的用途。
我只对 Python>=3.3 和隐式命名空间包感兴趣 - 没有__init__.py
的文件夹。
命名空间包是否应该只包含其他包,或者模块(即.py
文件)也“允许”?
命名空间包是否应该仅用作“容器”包,或者它们也可以包含在常规包中?
如果命名空间包仅作为容器有意义,我想我可以 state ,只要我有一个真正的 package 文件夹,它所有包含 python 模块的子文件夹也应该有__init__.py
?
# this is fine
ns_package/
+-- real_package/
+-- __init.py__
# how about this?
real_package/
+-- __init.py__ # I have it for docs AND want to force the dir to be a real package
+-- ns_package/ # I would just like to avoid an empty __init__.py
+-- amodule.py
我怀疑命名空间包仅作为容器才有意义,因为在另一种情况下,我将无法使用不同路径中的其他东西扩展命名空间,因为父级是一个真正的 package,必须在文件系统。 因此,我不会获得命名空间包的主要优势。
我之所以问,是因为在运行和导入模块时(从项目的根目录),在常规 package 内具有隐式命名空间 package 的情况可以正常工作。 但是,它需要对安装脚本进行一些调整,我想知道我是否首先做了一些有缺陷的事情。
注意:我尝试使用隐式命名空间包主要不是因为我想利用它们的功能,而是因为我讨厌空的__init__.py
文件。 我最初以为 python 3.3 终于摆脱了这个,包不再需要__init__.py
了,但似乎没有那么简单......
首先:您使用命名空间包的动机是有缺陷的。 空的__init__.py
文件没有问题; 它们现在可能是空的,但稍后可以填充内容。 即使它们保持空置也不会造成任何麻烦。
话虽如此,从技术上讲,将命名空间 package 放在常规 package 中并没有错。 当您执行import abc
形式的导入时,每个组件都会单独解析,并且b
可以是位于常规 ZEFE90A8E604A7C840E88D03A67F6B7D8 中a
命名空间 package。 考虑以下目录布局:
.
└── a
├── b
│ └── c.py
└── __init__.py
然后你可以导入模块c
:
>>> import a.b.c
>>> a
<module 'a' from '/tmp/a/__init__.py'>
>>> a.b
<module 'a.b' (namespace)>
>>> a.b.c
<module 'a.b.c' from '/tmp/a/b/c.py'>
如您所见,所有组件都是单独实例化的,其中命名空间ab
的__file__
属性设置为None
。
然而,这种设置阻止了命名空间包的主要用途,即它们可以拆分到多个目录中。 这是因为即使b
是命名空间 package,它也存在于常规 package a
中,该名称将缓存在sys.modules
,从而防止进一步搜索导入路径。 例如,考虑以下目录布局:
.
├── dir1
│ └── parent
│ ├── child
│ │ ├── one.py
│ ├── __init__.py
├── dir2
│ └── parent
│ ├── child
│ │ └── two.py
│ └── __init__.py
└── main.py
有两个命名空间包dir1/parent/child
和dir2/parent/child
。 但是,您只能使用其中一个,因为常规的 package dir1/parent
会阻止访问另一个。 让我们为main.py
尝试以下内容:
import sys
sys.path.extend(('dir1', 'dir1'))
import parent.child.one # this works
print(sys.modules['parent'])
print(sys.modules['parent.child'])
print(sys.modules['parent.child.one'])
import parent.child.two # this fails
我们将得到以下 output:
<module 'parent' from 'dir1/parent/__init__.py'>
<module 'parent.child' (namespace)>
<module 'parent.child.one' from 'dir1/parent/child/one.py'>
Traceback (most recent call last):
File "main.py", line 11, in <module>
import parent.child.two
ModuleNotFoundError: No module named 'parent.child.two'
这是因为sys.modules['parent']
是常规的 package,因此在import parent.child.two
中, parent
组件被解析为非常 package,它确实有一个属性child
,但这个命名空间不包含two
. 需要进一步搜索导入路径才能找到该模块。
1)您可以在命名空间 package 层次结构的任何级别拥有.py
文件。 只要它不包含__init__.py
文件,它就被认为是一个命名空间 package 并相应地解析其内容。 考虑以下目录布局:
.
└── a
├── b
│ ├── c
│ │ └── three.py
│ └── two.py
└── one.py
您可以在任何命名空间包中导入任何模块:
>>> import a.one
>>> import a.b.two
>>> import a.b.c.three
>>> a.b.c
<module 'a.b.c' (namespace)>
2)如上所述,您可以将命名空间包放在常规包中,但这没有多大意义,因为它会阻止它们的预期用途。
3)这在很大程度上取决于您所说的“应该”是什么意思。 从技术上讲, __init__.py
不是必需的,但它确实很有意义。
如开头所述, __init__.py
文件的目的不仅仅是指示常规的 python 包,而且它们通常也充满了内容。 如果没有,这没什么好担心的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.