用于导入模块时,Python 上 __init__ 中 __all__ 的行为

And*_*lva 5 python python-3.x

我有一个以下形式的python包:

package
??? __init__.py
??? module_1.py
??? module_2.py
Run Code Online (Sandbox Code Playgroud)

在 __init__.py 里面我有:

package
??? __init__.py
??? module_1.py
??? module_2.py
Run Code Online (Sandbox Code Playgroud)

Module_1 是:

__all__ = ["module_1", "module_2"]
Run Code Online (Sandbox Code Playgroud)

Module_2 是:

__all__ = ["foo"]
def foo(): pass
Run Code Online (Sandbox Code Playgroud)

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

__all__ = ["Bar"]
class Bar: pass
Run Code Online (Sandbox Code Playgroud)

但是,当我运行pk.foo()它时会引发错误:(AttributeError: module 'package' has no attribute 'foo'对于 Bar 也是如此)。

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

import package as pk
Run Code Online (Sandbox Code Playgroud)

以上工作。

但是,我不明白 文档。线路:

如果一个包的 __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()foo(),但都没有奏效。

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

一个工作示例看起来如何?.. __init__.py 的形式为 __all__ = ["module_1", "module_2"]。

And*_*lva 5

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

1)__all____init__和 模块上表现不同

1.1) 在一个模块内

_所有_ is within a _module_, it determines what objects are made available when runningfrom 模块导入 *`。

鉴于此包结构:

package
??? __init__.py
??? module_1.py
??? module_2.py
Run Code Online (Sandbox Code Playgroud)

并给出以下代码module_1

package
??? __init__.py
??? module_1.py
??? module_2.py
Run Code Online (Sandbox Code Playgroud)

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

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

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

__all__在 内时__init__

它被认为是列表模块名称应导入时,从包导入*遇到的。

运行from package import *将有两个效果:

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

这意味着如果__init__是以下形式:

__all__ = ["foo"]
def foo(): pass
def baz(): pass
Run Code Online (Sandbox Code Playgroud)

然后,运行from package import *将运行的脚本module_1module_2以及使这两个模块提供。所以,现在,foo里面的函数module_1可以调用 asmodule_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__可以包含以下代码:

__all__ = ["module_1", "module_2"]
Run Code Online (Sandbox Code Playgroud)

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

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

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

这种方法可能比1.2) 的方法更可取,在1.2)foo可以通过module_1.foo().


Bho*_*yar -3

代码中的关键问题是您__all__在子模块中定义了。它允许仅导出该模块,但此类模块在您的目录中不可用。它们是您想要使用的定义。

因此,只需从子模块中删除它们即可使其正常工作。