1369

我越来越多地使用 Python,并且不断看到__all__不同__init__.py文件中设置的变量。有人可以解释这是做什么的吗?

4

11 回答 11

1225

链接到,但这里没有明确提到,是什么时候__all__被使用。它是一个字符串列表,定义了在模块from <module> import *上使用时将导出模块中的哪些符号。

例如,a 中的以下代码foo.py显式导出符号barbaz

__all__ = ['bar', 'baz']

waz = 5
bar = 10
def baz(): return 'baz'

然后可以像这样导入这些符号:

from foo import *

print(bar)
print(baz)

# The following will trigger an exception, as "waz" is not exported by the module
print(waz)

如果__all__上面的内容被注释掉,则此代码将执行完成,因为默认行为import *是从给定的命名空间导入所有不以下划线开头的符号。

参考:https ://docs.python.org/tutorial/modules.html#importing-from-a-package

注意: __all__仅影响from <module> import *行为。中未提及的成员__all__仍然可以从模块外部访问,并且可以使用from <module> import <member>.

于 2008-09-15T15:49:50.213 回答
786

它是该模块的公共对象列表,由import *. 它覆盖了隐藏以下划线开头的所有内容的默认设置。

于 2008-09-04T21:30:46.213 回答
440

用 Python解释一切?

我不断看到__all__在不同__init__.py文件中设置的变量。

这是做什么的?

做什么__all__

它从模块中声明语义上的“公共”名称。如果 中存在名称__all__,则期望用户使用它,并且他们可以期望它不会改变。

它还将产生程序化效果:

import *

__all__在一个模块中,例如module.py

__all__ = ['foo', 'Bar']

意味着当您import *从模块中时,仅__all__导入其中的那些名称:

from module import *               # imports foo and Bar

文档工具

文档和代码自动完成工具可能(事实上,应该)也检查__all__以确定哪些名称可以从模块中显示为可用。

__init__.py使目录成为 Python 包

文档

需要这些__init__.py文件才能使 Python 将目录视为包含包;这样做是为了防止具有通用名称(例如字符串)的目录无意中隐藏了稍后出现在模块搜索路径上的有效模块。

在最简单的情况下,__init__.py可以只是一个空文件,但它也可以执行包的初始化代码或设置__all__变量。

所以__init__.py可以__all__为一个声明。

管理 API:

一个包通常由可以相互导入的模块组成,但它们必须与一个__init__.py文件绑定在一起。该文件使该目录成为实际的 Python 包。例如,假设您在一个包中有以下文件:

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

让我们使用 Python 创建这些文件,以便您可以继续进行操作 - 您可以将以下内容粘贴到 Python 3 shell 中:

from pathlib import Path

package = Path('package')
package.mkdir()

(package / '__init__.py').write_text("""
from .module_1 import *
from .module_2 import *
""")

package_module_1 = package / 'module_1.py'
package_module_1.write_text("""
__all__ = ['foo']
imp_detail1 = imp_detail2 = imp_detail3 = None
def foo(): pass
""")

package_module_2 = package / 'module_2.py'
package_module_2.write_text("""
__all__ = ['Bar']
imp_detail1 = imp_detail2 = imp_detail3 = None
class Bar: pass
""")

现在您已经提供了一个完整的 api,其他人在导入您的包时可以使用它,如下所示:

import package
package.foo()
package.Bar()

并且该包不会包含您在创建模块时使用的所有其他实现细节,从而使package命名空间变得混乱。

__all____init__.py

经过更多的工作,也许您已经决定模块太大(比如数千行?)并且需要拆分。因此,您执行以下操作:

package
├── __init__.py
├── module_1
│   ├── foo_implementation.py
│   └── __init__.py
└── module_2
    ├── Bar_implementation.py
    └── __init__.py

首先使子包目录与模块同名:

subpackage_1 = package / 'module_1'
subpackage_1.mkdir()
subpackage_2 = package / 'module_2'
subpackage_2.mkdir()

移动实现:

package_module_1.rename(subpackage_1 / 'foo_implementation.py')
package_module_2.rename(subpackage_2 / 'Bar_implementation.py')

__init__.py为声明__all__for each的子包创建s:

(subpackage_1 / '__init__.py').write_text("""
from .foo_implementation import *
__all__ = ['foo']
""")
(subpackage_2 / '__init__.py').write_text("""
from .Bar_implementation import *
__all__ = ['Bar']
""")

现在您仍然在包级别配置了 api:

>>> import package
>>> package.foo()
>>> package.Bar()
<package.module_2.Bar_implementation.Bar object at 0x7f0c2349d210>

而且您可以轻松地向您的 API 添加可以在子包级别而不是子包的模块级别管理的内容。如果您想为 API 添加一个新名称,您只需更新__init__.py,例如在 module_2 中:

from .Bar_implementation import *
from .Baz_implementation import *
__all__ = ['Bar', 'Baz']

如果您还没有准备好在顶级 API 中发布Baz,那么在您的顶级 API 中,__init__.py您可以:

from .module_1 import *       # also constrained by __all__'s
from .module_2 import *       # in the __init__.py's
__all__ = ['foo', 'Bar']     # further constraining the names advertised

如果您的用户知道 的可用性Baz,他们可以使用它:

import package
package.Baz()

但如果他们不知道,其他工具(如pydoc)不会通知他们。

您可以稍后更改何时Baz准备好进入黄金时段:

from .module_1 import *
from .module_2 import *
__all__ = ['foo', 'Bar', 'Baz']

前缀___all__

默认情况下,Python 将导出所有不以_. 你当然可以依赖这种机制。事实上,Python 标准库中的一些包确实依赖于此,但为此,它们为其导入设置了别名,例如,在ctypes/__init__.py

import os as _os, sys as _sys

使用_约定可以更优雅,因为它消除了再次命名名称的冗余。但是它增加了导入的冗余(如果你有很多的话)并且很容易忘记始终如一地这样做 - 你想要的最后一件事是必须无限期地支持你打算只是一个实现细节的东西,只是因为您_在命名函数时忘记了前缀 an 。

我个人在模块开发生命周期的早期编写了一个__all__模块,以便可能使用我的代码的其他人知道他们应该使用和不使用什么。

标准库中的大多数包也使用__all__.

当避免__all__有意义时

_坚持使用前缀约定代替以下时间是有意义的__all__

  • 您仍处于早期开发模式并且没有用户,并且不断地调整您的 API。
  • 也许您确实有用户,但是您有涵盖 API 的单元测试,并且您仍在积极地添加到 API 并在开发中进行调整。

装饰export

使用的缺点__all__是您必须编写两次导出的函数和类的名称 - 并且信息与定义分开。我们可以使用装饰器来解决这个问题。

我从 David Beazley 关于包装的演讲中得到了这样一个出口装饰器的想法。这个实现似乎在 CPython 的传统导入器中运行良好。如果你有一个特殊的导入钩子或系统,我不保证,但如果你采用它,退出是相当简单的 - 你只需要手动将名称添加回__all__

因此,例如,在实用程序库中,您将定义装饰器:

import sys

def export(fn):
    mod = sys.modules[fn.__module__]
    if hasattr(mod, '__all__'):
        mod.__all__.append(fn.__name__)
    else:
        mod.__all__ = [fn.__name__]
    return fn

然后,在你定义一个 的地方__all__,你这样做:

$ cat > main.py
from lib import export
__all__ = [] # optional - we create a list if __all__ is not there.

@export
def foo(): pass

@export
def bar():
    'bar'

def main():
    print('main')

if __name__ == '__main__':
    main()

无论是作为 main 运行还是由另一个函数导入,这都可以正常工作。

$ cat > run.py
import main
main.main()

$ python run.py
main

并且 API 配置import *也将起作用:

$ cat > run.py
from main import *
foo()
bar()
main() # expected to error here, not exported

$ python run.py
Traceback (most recent call last):
  File "run.py", line 4, in <module>
    main() # expected to error here, not exported
NameError: name 'main' is not defined
于 2016-02-29T21:58:50.740 回答
190

为了准确起见,我只是添加了这个:

所有其他答案均指模块__all__文件中明确提到的原始问题__init__.py,所以这是关于 python packages

通常,__all__仅在使用语句的from xxx import *变体时才起作用。import这适用于包和模块。

其他答案中解释了模块的行为。此处详细描述了包的确切行为。

简而言之,__all__在包级别上与模块做的事情大致相同,除了它处理内的模块 (与在模块内指定名称相反)。所以__all__指定我们使用时应加载并导入当前命名空间的所有模块from package import *

最大的区别是,当您省略__all__package's中的声明时__init__.py,该语句from package import *根本不会导入任何内容(文档中解释了例外情况,请参见上面的链接)。

另一方面,如果您__all__在模块中省略,“星号导入”将导入模块中定义的所有名称(不以下划线开头)。

于 2013-05-16T19:01:48.843 回答
92

它还改变了 pydoc 将显示的内容:

模块1.py

a = "A"
b = "B"
c = "C"

模块2.py

__all__ = ['a', 'b']

a = "A"
b = "B"
c = "C"

$ pydoc模块1

关于模块 module1 的帮助:

姓名
    模块1

文件
    模块1.py

数据
    a = 'A'
     b = 'B'
     c = 'C'

$ pydoc模块2

关于模块 module2 的帮助:

姓名
    模块2

文件
    模块2.py

数据
    __all__ = ['a', 'b']
     a = 'A'
     b = 'B'

__all__在我的所有模块中声明,以及强调内部细节,当你在现场口译会话中使用以前从未使用过的东西时,这些真的很有帮助。

于 2010-05-15T03:22:29.817 回答
77

__all__*from <module> import * 和中自定义from <package> import *


模块.py要导入的文件。

是一个包含文件的目录__init__.py。一个包通常包含模块。


模块

""" cheese.py - an example module """

__all__ = ['swiss', 'cheddar']

swiss = 4.99
cheddar = 3.99
gouda = 10.99

__all__让人们知道模块的“公共”特性。[ @AaronHall ] 另外,pydoc 可以识别它们。[ @Longpoke ]

模块导入 *

查看swiss和如何cheddar被带入本地命名空间,但不是gouda

>>> from cheese import *
>>> swiss, cheddar
(4.99, 3.99)
>>> gouda
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'gouda' is not defined

如果没有__all__,任何符号(不以下划线开头)都将可用。


进口*不受影响__all__


导入模块

>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)

模块导入名称

>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)

导入模块作为本地名称

>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)

包裹

__init__.py的文件中是带有公共模块或其他对象名称的字符串列表。这些功能可用于通配符导入。与模块一样,自定义何时从包中导入通配符。[ @MartinStettner ] __all____all__*

这是Python MySQL 连接器 的摘录__init__.py

__all__ = [
    'MySQLConnection', 'Connect', 'custom_error_exception',

    # Some useful constants
    'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
    'HAVE_CEXT',

    # Error handling
    'Error', 'Warning',

    ...etc...

    ]

默认情况下,asterisk with no __all__for a package很复杂,因为明显的行为会很昂贵:使用文件系统搜索包中的所有模块。相反,在我阅读文档时,只__init__.py导入了定义的对象:

如果__all__未定义,则该语句from sound.effects import *不会包中的所有子模块导入sound.effects当前命名空间;它仅确保sound.effects已导入包(可能在 中运行任何初始化代码__init__.py),然后导入包中定义的任何名称。这包括由__init__.py. 它还包括先前导入语句显式加载的包的任何子模块。


最后,一个受人尊敬的堆栈溢出答案、教授和随处可见的人的传统,是首先提出问题的责备

通配符导入......应该避免,因为它们[混淆]读者和许多自动化工具。

[ PEP 8,@ToolmakerSteve]

于 2016-03-20T20:20:43.033 回答
56

来自(非官方的)Python 参考 Wiki

模块定义的公共名称是通过检查模块命名空间中的变量来确定的__all__;如果已定义,它必须是由该模块定义或导入的名称的字符串序列。中给出的名称__all__都被认为是公开的,并且必须存在。如果__all__未定义,则公共名称集包括在模块名称空间中找到的所有名称,这些名称不以下划线字符(“_”)开头。__all__应该包含整个公共 API。它旨在避免意外导出不属于 API 的项目(例如在模块中导入和使用的库模块)。

于 2008-09-04T21:31:16.370 回答
14

简短的回答

__all__影响from <module> import *陈述。

长答案

考虑这个例子:

foo
├── bar.py
└── __init__.py

foo/__init__.py

  • (隐式)如果我们不定义__all__,那么from foo import *只会导入foo/__init__.py.

  • (显式)如果我们定义__all__ = [],则from foo import *不会导入任何内容。

  • (显式)如果我们定义__all__ = [ <name1>, ... ],则from foo import *只会导入这些名称。

请注意,在隐式情况下,python 不会导入以_. 但是,您可以使用__all__.

您可以在此处查看 Python 文档。

于 2018-04-03T01:19:41.487 回答
9

__all__用于记录 Python 模块的公共 API。虽然它是可选的,__all__但应该使用。

以下是Python 语言参考的相关摘录:

模块定义的公共名称是通过检查模块命名空间中的变量来确定的__all__;如果已定义,它必须是由该模块定义或导入的名称的字符串序列。中给出的名称__all__都被认为是公开的,并且必须存在。如果__all__未定义,则公共名称集包括在模块名称空间中找到的所有名称,这些名称不以下划线字符 ('_') 开头。__all__应该包含整个公共 API。它旨在避免意外导出不属于 API 的项目(例如在模块中导入和使用的库模块)。

PEP 8使用了类似的措辞,尽管它也明确指出,导入的名称在不__all__存在时不是公共 API 的一部分:

为了更好地支持自省,模块应该使用__all__属性在其公共 API 中显式声明名称。设置__all__为空列表表示该模块没有公共 API。

[...]

导入的名称应始终被视为实现细节。其他模块不得依赖对此类导入名称的间接访问,除非它们是包含模块 API 的明确记录的部分,例如,os.path或者__init__从子模块公开功能的包模块。

此外,正如其他答案中所指出的,__all__用于启用包的通配符导入

import 语句使用以下约定:如果包的__init__.py代码定义了一个名为 的列表,则将其视为遇到__all__时应导入的模块名称的列表。from package import *

于 2016-04-26T01:39:25.020 回答
9

__all__影响如何from foo import *工作。

位于模块主体内(但不在函数或类主体内)的代码可以在语句中使用星号 ( *) :from

from foo import *

请求将模块的*所有属性foo(以下划线开头的除外)绑定为导入模块中的全局变量。当foo有一个属性__all__时,该属性的值是由这种类型的from语句绑定的名称列表。

如果foo是一个并且它__init__.py定义了一个名为 的列表,它被认为是遇到__all__时应该导入的子模块名称的列表。from foo import *如果__all__未定义,则该语句from foo import *导入包中定义的任何名称。这包括由__init__.py.

请注意,__all__不一定是列表。根据importstatement上的文档,如果已定义,则__all__必须是一个字符串序列,这些字符串是模块定义或导入的名称。因此,您不妨使用元组来节省一些内存和 CPU 周期。如果模块定义了一个公共名称,请不要忘记逗号:

__all__ = ('some_name',)

另请参阅为什么“import *”不好?

于 2019-01-09T14:48:45.013 回答
3

这是在 PEP8 中定义

全局变量名

(我们希望这些变量只在一个模块中使用。)约定与函数的约定大致相同。

设计用于 via 的模块from M import *应使用__all__防止导出全局变量的机制,或使用在此类全局变量前加下划线的旧约定(您可能希望这样做以指示这些全局变量是“模块非公共”)。

PEP8 为包含主要 Python 发行版中标准库的 Python 代码提供编码约定。你越遵循这一点,你就越接近最初的意图。

于 2019-06-17T17:55:21.017 回答