如何在(子)模块中使用 __init__.py 来定义命名空间?

Posted

技术标签:

【中文标题】如何在(子)模块中使用 __init__.py 来定义命名空间?【英文标题】:How to use __init__.py in (sub-)modules to define namespaces? 【发布时间】:2017-06-11 19:02:07 【问题描述】:

我的问题是关于编写 Python (3.x) 包和(子)模块以及正确使用 __init__.py 文件来声明命名空间。 我以前用 C++ 编写代码,所以我喜欢使用很多单独的文件来组织项目。例如,(imo) 如果一个模块包含多个类,则每个类都应该在一个单独的文件中。

由于我缺乏 Python 开发经验,因此很难用一个简单的问题来表达我的想法。 所以我们以下面这个小python包为例。

目录布局

dir
 |
 +-- example
 |    |
 |    +-- script.py
 |
 +-- package
      |
      +-- __init__.py
      |
      +-- foo.py
      |
      +-- subpackage
           |
           +-- __init__.py
           |
           +-- bar.py

让我们看一下文件。

文件内容

package/foo.py:

def foo:
    print('foo')

package/subpackage/bar.py:

def bar:
    print('bar')

以下example/script.py 工作正常。

import sys
sys.path.insert(0, '..')

import package
import package.subpackage


package.foo.foo()
package.subpackage.bar.bar()

我不喜欢使用package.foo.foo() / package.subpackage.bar.bar(),我想使用package.foo() / package.subpackage.bar()

而且我不想使用from package.subpackage.bar import bar,因为我不想将子包的命名空间混入脚本中。

解决方案

我使用__init__.py 文件来实现这一点。

package/__init__.py:

from package.foo import foo

package/subpackage/__init__.py:

from package.subpackage.bar import bar

问题

这是定义命名空间的好方法吗?或者有没有更好/通用的方法来组织 python 中包的文件系统。 (我没有找到合适的教程/示例。)

在文件package/subpackage/__init__.py 中,为什么必须是:

from package.subpackage.bar import bar

而不是:

from subpackage.bar import bar?

这会导致错误:

Traceback (most recent call last):
File "script.py", line x, in <module>
import package.subpackage
File "..\package\subpackage\__init__.py", line x, in <module>
from subpackage.bar import bar
ImportError: No module named 'subpackage'

【问题讨论】:

【参考方案1】:

这是定义命名空间的好方法吗?或者有没有更好/通用的方法来组织 python 中包的文件系统。 (我没有找到合适的教程/示例。)

这是设置 Python 包的一种好的方法。由于foo.pybar.py 的内容,只有好的 而不是“好”。

我不喜欢使用package.foo.foo() / package.subpackage.bar.bar(),我想使用package.foo() / package.subpackage.bar()

在这种情况下,你不能。并且不混合名称空间是不使用from package.subpackage.bar import bar 的好理由。

如果def foo(): ...def bar(): ... 直接在__init__.py 中会更好。这样,您就可以完成package.foo()package.subpackage.bar()。也可以通过在 init 中使用 __all__ 来完成,但 import * 也被认为不好。

或者,foobar 包应该包含更多内容,例如 foo.function1foo.other_funcbar.drink 等,这使其组织更加人性化且易于理解。

示例和参考不在 *** 好问题的范围内,但这里有一些,适合深思熟虑的问题:

Structuring Your Project — The Hitchhiker's Guide to Python The Pythonic way of organizing modules and packages How to organize a Python Project?

【讨论】:

感谢您的回答。基本上你的第二个链接(“Pythonic 方式......”)是我正在寻找的 *** 问题。我不喜欢作者所说的在一个文件中“抛出大量代码”的想法。但由于 Python 将文件视为模块,因此似乎没有好的解决方案。我想到了编辑__init__.py-files here。【参考方案2】:

第二个问题的答案:

在文件package/subpackage/__init__.py中,为什么必须是:

from package.subpackage.bar import bar

而不是:

from subpackage.bar import bar?

是你必须使用from .bar import bar。当您想在包结构中进行相对导入时,使用点。见:https://docs.python.org/3/tutorial/modules.html#intra-package-references

【讨论】:

以上是关于如何在(子)模块中使用 __init__.py 来定义命名空间?的主要内容,如果未能解决你的问题,请参考以下文章

我可以使用 __init__.py 来定义全局变量吗?

如何递归获取 python 包中的所有子模块?

包 __init__.py 的相对导入

Python包

python 如何调用子文件下的模块

在 __main__.py 中使用模块自己的对象