首页 > 解决方案 > 用于导入模块时,Python 上 __init__ 中 __all__ 的行为

问题描述

我有一个形式的python包:

package
├── __init__.py
├── module_1.py
└── module_2.py

在 __init__.py 我有:

__all__ = ["module_1", "module_2"]

Module_1 是:

__all__ = ["foo"]
def foo(): pass

Module_2 是:

__all__ = ["Bar"]
class Bar: pass

文档中我认为以下代码将导入fooBar

import package as pk

但是,当我运行pk.foo()它时会引发错误:(AttributeError: module 'package' has no attribute 'foo'与 Bar 相同)。

感谢这个答案,我知道要获得所需的行为,我可以将 __init__.py 更改为:

from .module_1 import *
from .module_2 import *

以上工作。

但是,我不理解 文档。这些行:

如果一个包的 __init__.py 代码定义了一个名为 __all__ 的列表,它被认为是遇到 from package import * 时应该导入的模块名称的列表

听起来我原来的 __init__.py 应该可以工作(带有 的那个__all__ = ["module_1", "module_2"])。也就是说,该行import package as pk应该已经导入了模块 module_1module_2,这反过来又使fooBar可用。

我错过了什么?

编辑:

我也尝试过使用文档中提到的内容。也就是说,使用from package import *, 然后尝试使用package.foo()and foo(),但都没有奏效。

第一个 ,package.foo()抛出错误NameError: 'package' is not defined.。第二个,foo(),抛出错误NameError: 'foo' is not defined.

一个工作示例会是什么样子?.. __init__.py 的形式为 __all__ = ["module_1", "module_2"]。

标签: pythonpython-3.x

解决方案


我将尝试总结从对我的问题、文档、我自己的测试和这篇文章的评论中获得的知识。

1)在和模块__all__上表现不同__init__

1.1) 在一个模块内

_所有_is within a _module_, it determines what objects are made available when running从模块导入 *`.

鉴于此包结构:

package
├── __init__.py
├── module_1.py
└── module_2.py

并给出以下代码module_1

__all__ = ["foo"]
def foo(): pass
def baz(): pass

运行from package.module_1 import *将使foo可用但不可baz用。

此外,foo可以使用 调用foo(),即不需要引用模块。

1.2)内__init__(我原来的问题)

什么时候__all__在 内__init__

它被视为遇到from package import *时应导入的模块名称列表。

跑步from package import *会有两个效果:

  1. 将运行模块的脚本__all__(它们被导入)。
  2. 这些模块在命名空间中可用。

这意味着 if__init__的形式为:

__all__ = ["module_1", "module_2"]

然后, runningfrom package import *将运行 和 的脚本并使module_1两个模块都可用。所以,现在,里面的函数可以调用 as而不是.module_2foomodule_1module_1.foo() package.module_1.foo()

但是,如果这是意图,那么使用from package.module_1 import foo可能会更好。因为它foo可以作为foo().

2)from package import *不一样import package

运行from package import *1.2)中提到的效果。但是,这不适用于运行import package:即module_1.foo()在这种情况下不起作用。

3)替代方法__init__

(以下内容基于这篇文章)正如我在问题中提到的,还有一种替代方法__init__,其中您希望在用户调用时可用的对象from package import *直接导入到__init__.

例如,__init__可能包含以下代码:

from .module_1 import *
from .module_2 import *

然后,当用户调用from package import *模块 1 和 2 中的对象时,命名空间上将可用。

如果module_11.1),则foo可以在不引用模块的情况下调用该函数。即foo()。但是,这对baz.

2)中所述,from package import *与 不同import package。在这种情况下调用import package(使用 this __init__)使 foo 可以通过package.foo()不是foo(). 同样import package as pk使 foo 作为pk.foo().

这种方法可能比1.2)的方法更可取,后者foo通过 提供module_1.foo()


推荐阅读