繁体   English   中英

相对 position 和 Python 中命名空间包的内容 >= 3.3

[英]relative position and content of namespace packages in Python >= 3.3

我阅读了文档和相当多的 stackoverflow 帖子,但没有找到明确的答案来解决我的疑问。

我想我了解命名空间包的用途。

我只对 Python>=3.3 和隐式命名空间包感兴趣 - 没有__init__.py的文件夹。

问题

  1. 命名空间包是否应该只包含其他包,或者模块(即.py文件)也“允许”?

  2. 命名空间包是否应该仅用作“容器”包,或者它们也可以包含在常规包中

  3. 如果命名空间包仅作为容器有意义,我想我可以 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/childdir2/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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM