在Python中创建命名空间包的方法

时间:2011-12-05 02:25:45

标签: python setuptools module-packaging

Namespace Packages in distribute开始,我知道我可以使用命名空间包将一个大的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

1 个答案:

答案 0 :(得分:41)

当导入子包时,命名空间包主要具有特定的效果。基本上,这是导入foo.bar

时发生的情况
  • 导入程序扫描sys.path,查找类似foo的内容。
  • 当找到某些内容时,它会在已发现的foo bar内查看。
  • 如果找不到bar
    1. 如果foo是普通包,则会引发ImportError,表示foo.bar不存在。
    2. 如果foo名称空间包,则导入程序会返回查看sys.path以查找foo的下一个匹配项。只有在所有路径都已耗尽时才会引发ImportError

这就是它所做的,但不解释你为什么会这样想。假设您设计了一个大而有用的库(foo),但作为其中的一部分,您还开发了一个小但非常有用的实用程序(foo.bar),其他python程序员发现它们很有用,即使它们没有t用于更大的图书馆。

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

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

所以你真正想要的是一种单独安装foo.bar的方法,但是当需要时也很乐意与foo共存。

命名空间包允许这两个完全独立的foo包安装可以共存。 setuptools会认识到这两个软件包的设计是彼此相邻的,并礼貌地移动文件夹/文件,使两者都在路径上并显示为foo,其中一个包含{{1}另一个包含foo.bar的其余部分。

您将拥有两个不同的foo脚本,每个脚本一个。两个包中的setup.py必须表明它们是名称空间包,因此无论首先发现哪个包,导入器都知道继续。