14

Distributed 中的命名空间包中,我知道我可以利用命名空间包将一个大的 Python 包分成几个较小的包。真是太棒了。该文件还提到:

请注意,顺便说一句,您的项目的源代码树必须包含命名空间包的 __init__.py 文件(以及任何父包的 __init__.py),在正常的 Python 包布局中。这些__init__.py 文件必须包含以下行:

__import__('pkg_resources').declare_namespace(__name__)

此代码确保命名空间包机制正在运行,并且当前包已注册为命名空间包。

我想知道将相同的目录层次结构与包的层次结构保持一致有什么好处吗?或者,这只是distribute/setuptools的命名空间包功能的技术要求?

前任,

我想提供一个子包foo.bar,这样我必须构建以下文件夹层次结构并准备一个 __init__.py 以使 setup.py 工作于命名空间包:

~foo.bar/
~foo.bar/setup.py
~foo.bar/foo/__init__.py    <=    one-lined file dedicated to namespace packages
~foo.bar/foo/bar/__init__.py
~foo.bar/foo/bar/foobar.py

我不熟悉命名空间包,但在我看来 1) foo/bar 和 2)(几乎)单行 __init__.py 是例行任务。他们确实提供了一些“这是一个命名空间包”的提示,但我认为我们已经在setup.py中获得了这些信息?

编辑:

如以下块所示,我可以在我的工作目录中拥有一个没有嵌套目录和单行 __init__.py 的命名空间包吗?也就是说,我们可以让setup.py只输入一行就自动生成这些namespace_packages = ['foo']吗?

~foo.bar/
~foo.bar/setup.py
~foo.bar/src/__init__.py    <=    for bar package
~foo.bar/src/foobar.py
4

1 回答 1

45

命名空间包主要在导入子包时发挥特殊作用。基本上,这就是导入时会发生的情况foo.bar

  • 进口商通过扫描sys.path寻找看起来像的东西foo
  • 当它找到某些东西时,它会在 found foofor内部查找bar
  • 如果bar没有找到:
    1. 如果foo是普通包,ImportError则引发 an,表示foo.bar不存在。
    2. 如果foo命名空间包,则导入器会返回查找sys.path下一个匹配的foo. ImportError仅当所有路径都已用尽时才会出现。

所以这就是它的作用,但没有解释为什么你可能想要那个。假设您设计了一个大而有用的foofoo.bar

即使大多数使用它的人只导入子模块,您也可以将它们作为一个大包(如您设计的那样)一起分发。您的用户会发现这非常不方便,因为他们必须下载整个内容(全部 200MB!),即使他们只对 10 行实用程序类真正感兴趣。如果你有一个开放的许可证,你可能会发现有几个人最终分叉了它,现在你的实用程序模块有六个不同的版本。

您可以重写整个库,以便该实用程序位于foo命名空间之外(bar而不是foo.bar)。您将能够单独分发该实用程序,并且您的一些用户会很高兴,但这是很多工作,特别是考虑到实际上很多用户使用整个库,因此他们将不得不重写他们的使用新的程序。

因此,您真正想要的是一种foo.bar自行安装的方式,但也可以在需要时愉快地共存foo

命名空间包正好允许这样,一个包的两个完全独立的安装foo可以共存。 setuptools将认识到这两个包被设计为彼此相邻,并礼貌地移动文件夹/文件,使两者都在路径上并显示为foo,一个包含foo.bar,另一个包含foo.

您将有两个不同的setup.py脚本,每个脚本一个。 foo/__init__.py在这两个包中必须指出它们是命名空间包,以便导入器知道无论首先发现哪个包都可以继续。

于 2011-12-05T02:51:25.970 回答