__all__ Python 中是什么意思?

What does __all__ mean in Python?

提问人:varikin 提问时间:9/5/2008 最后编辑:Mateen Ulhaqvarikin 更新时间:7/6/2023 访问量:607479

问:

我在文件中看到。它有什么作用?__all____init__.py

Python 语法 命名空间

评论


答:

848赞 Jimmy 9/5/2008 #1

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

评论

190赞 Brandon Rhodes 12/9/2009
以下划线开头的对象,或未在 if 中提及的对象,则不完全隐藏;如果您知道他们的名字,他们通常可以完全看到和访问。只有在“进口*”的情况下(无论如何都不建议这样做),这种区别才有意义。__all____all__
35赞 flying sheep 4/8/2012
@BrandonRhodes:这也不完全正确:建议只导入您知道为其设计的模块(例如)。如果是这种情况,一个很好的提示是模块代码中存在以下划线或以下划线开头的名称。import *tk__all__
22赞 debug 4/10/2018
公共接口和内部接口 - python.org/dev/peps/pep-0008/#id50, 为了更好地支持自省,模块应使用 __all__ 属性在其公共 API 中显式声明名称。将__all__设置为空列表表示该模块没有公共 API。
5赞 chepner 11/6/2019
我不确定如果今天(甚至在 2012 年)发布,推荐的做法是使用 .我认为这种做法是由于惯性而被接受的,而不是有意的设计。tkfrom tk import *
9赞 JP Zhang 5/7/2021
总结一下:如果有 ,将导入 中的所有内容,否则,它将导入所有不以下划线开头的内容。__all__import *__all__
1518赞 Alec Thomas 9/15/2008 #2

链接到,但这里没有明确提及,是确切的使用时间。它是一个字符串列表,用于定义在模块上使用时将导出模块中的哪些符号。__all__from <module> import *

例如,a 中的以下代码显式导出符号和:foo.pybarbaz

__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>

评论

24赞 Alec Thomas 7/22/2020
目的是说明符号已导出。它是否执行函数是次要的。
73赞 Julio Cezar Silva 8/7/2020
我发现令人费解的是,直到今天,还没有一种方法可以通过直接引用函数/对象来填充。相反,我们必须输入他们的名字,并在名称更改时单独更正它们。对于活动代码库来说,似乎很容易出错。__all__
20赞 phsyron 9/30/2020
@JulioCezarSilva题外话了,但值得注意的是,对于类和函数,您可以使用该属性__name__
2赞 Yair M 9/16/2021
@phsyron 虽然旅游建议似乎是合法的,但它不需要已经导入模块吗?我问是因为通过这种方式填充,我仍然会收到有关从 mypy 隐式导入的警告,而通过显式输入,我不会。__all__
4赞 Xbox One 6/29/2022
@ArtificiallyIntelligence 来自 PEP 8:“通配符导入有一个可防御的用例,即将内部接口重新发布为公共 API 的一部分。此外,问题和答案是关于所有特殊变量的使用,并且不认可通配符导入。这就是所有用途。
97赞 L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ 5/15/2010 #3

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

module1.py

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

module2.py

__all__ = ['a', 'b']

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

$ pydoc 模块1

Help on module module1:

NAME
    module1

FILE
    module1.py

DATA
    a = 'A'
    b = 'B'
    c = 'C'

$ pydoc 模块2

Help on module module2:

NAME
    module2

FILE
    module2.py

DATA
    __all__ = ['a', 'b']
    a = 'A'
    b = 'B'

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

198赞 MartinStettner 5/17/2013 #4

准确地说,我只是添加这个:

所有其他答案都涉及模块。原始问题在文件中明确提到,所以这是关于 python 的。__all____init__.py

通常,只有在使用语句的变体时才会起作用。这既适用于软件包,也适用于模块。__all__from xxx import *import

模块的行为在其他答案中进行了解释。此处详细介绍了包的确切行为。

简而言之,在包级别上执行与模块大致相同的操作,只是它处理包中的模块(与在模块中指定名称相反)。所以指定了当我们使用 时应加载并导入到当前命名空间中的所有模块。__all____all__from package import *

最大的区别在于,当您省略 in a package 的声明时,该语句根本不会导入任何内容(文档中解释的例外情况,请参阅上面的链接)。__all____init__.pyfrom package import *

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

评论

32赞 Nikratio 7/21/2014
from package import *仍然会导入 中定义的所有内容,即使没有 .重要的区别在于,如果没有它,它不会自动导入包目录中定义的任何模块。__init__.pyall__all__
1赞 variable 10/12/2019
当我们使用 all 包含 [foo, bar] 并在 test.py 文件中时,如果我们使用: from package import *,那么,foo 和 bar 是在 test.py 的本地命名空间中还是在 foo 和 bars 自己的命名空间中导入?
0赞 C S 9/26/2023
这个答案是不正确的。引用的文档实际上打算“模块名称”是指模块命名空间中的名称,而不是包中模块的名称(参见 github.com/python/cpython/issues/106556)。
586赞 Russia Must Remove Putin 3/1/2016 #5

用 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 包

文档中:

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

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

所以可以声明一个__init__.py__all__

管理 API:

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

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

让我们使用 Python 创建这些文件,以便您可以按照 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,其他人在导入您的包时可以使用该 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')

为声明 for each 的子包创建 s:__init__.py__all__

(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:And now you have the api provisioned at the package level:

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

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

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

如果你还没有准备好在顶级 API 中发布,在你的顶级 API 中,你可以拥有:Baz__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 将导出所有在导入时不以 an 开头的名称。正如此处的 shell 会话所示,不会从模块中引入名称:_import *import *_us_non_publicus.py

$ cat us.py
USALLCAPS = "all caps"
us_snake_case = "snake_case"
_us_non_public = "shouldn't import"
$ python
Python 3.10.0 (default, Oct  4 2021, 17:55:55) [GCC 10.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from us import *
>>> dir()
['USALLCAPS', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'us_snake_case']

你当然可以依赖这个机制。事实上,Python 标准库中的一些包确实依赖于此,但要做到这一点,它们会为其导入添加别名,例如,在 ctypes/__init__.py 中:

import os as _os, sys as _sys

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

我个人在开发生命周期的早期就为模块编写了一个模块,以便其他可能使用我的代码的人知道他们应该使用什么,不应该使用什么。__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

评论

1赞 MvG 1/27/2017
交叉引用:我在这个 CW 回答中提到了你的装饰者,回答了如何编写装饰者的问题。@export
70赞 Brett Reinhard 11/7/2017
这是我见过的最有用的答案,可以帮助相对较新的 python 开发人员了解导入模块/包的过程以及使用__init__.py__all__
0赞 Mike C 8/13/2019
这对我有很大帮助。我的问题是,我想导入的子模块都是生成的文件,它们的符号中有很多粗糙的东西,我想去掉这些文件,而不必手动确保这是正确的。__all__
1赞 Russia Must Remove Putin 3/28/2022
我澄清说:“默认情况下,Python 将导出所有在导入时不以 _ 开头的名称。 我添加了一个 shell 会话,该会话演示了现在所述的这种方式。import *
1赞 nigh_anxiety 9/1/2023
@ruslaniv 在我看来,该语句的意图是使包被定义为与它导入的内容完全匹配,同时不包括作为其导入一部分存在于包中的任何其他符号,如果有人在哪里做“从包导入*。例如,如果包包含其他测试脚本或不应直接访问的第 3 个子模块,则除非显式导入,否则将阻止导入这些文件中的符号。__all__from module1 import *from module2 import *
91赞 Bob Stein 3/21/2016 #6

__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]

模块导入 *

查看如何 和 被引入本地命名空间,但不是:swisscheddargouda

>>> 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)

模块导入为 localname

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

在包的文件中是一个字符串列表,其中包含公共模块或其他对象的名称。这些功能可用于通配符导入。与模块一样,自定义从包中导入通配符时。[@MartinStettner]__init__.py__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...

    ]

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

如果未定义,则语句不会将包中的所有子模块导入到当前命名空间中;它只确保包已导入(可能运行 中的任何初始化代码),然后导入包中定义的任何名称。这包括由 定义(和显式加载)的任何名称。它还包括由以前的 import 语句显式加载的包的任何子模块。__all__from sound.effects import *sound.effectssound.effects__init__.py__init__.py


最后,一个受人尊敬的传统是堆栈溢出的答案,教授和无处不在的漫不经心的人,是首先提出问题的责备的好兆头

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

[PEP 8, @ToolmakerSteve]

评论

2赞 radzak 3/21/2019
我真的很喜欢这个答案,但我缺少有关没有导入任何模块的默认行为的信息。from <package> import *__all____init__.py
0赞 Bob Stein 3/22/2019
感谢@Jatimir,我尽我所能澄清了,没有进行实验。我几乎想说这种情况(没有包的所有星号)的行为__init__.py模块的行为相同。但我不确定这是否准确,或者特别是如果排除了下划线前缀的对象。此外,我更清楚地将 MODULES 和 PACKAGES 部分分开。你的想法?
15赞 Mihai Capotă 4/26/2016 #7

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

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

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

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

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

[...]

导入的名称应始终被视为实现详细信息。其他模块不得依赖于对此类导入名称的间接访问,除非它们是包含模块的 API 的显式记录部分,例如公开子模块功能的包模块。os.path__init__

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

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

19赞 Cyker 4/3/2018 #8

简答

__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 文档。

12赞 Eugene Yarmash 1/9/2019 #9

__all__影响工作方式。from foo import *

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

from foo import *

请求将模块的所有属性(以下划线开头的属性除外)绑定为导入模块中的全局变量。当具有属性时,该属性的值是受此类语句绑定的名称的列表。*foofoo__all__from

如果是一个,并且它定义了一个名为 的列表,则将其视为遇到时应导入的子模块名称列表。如果未定义,则该语句将导入包中定义的任何名称。这包括由 定义(和显式加载)的任何名称。foo__init__.py__all__from foo import *__all__from foo import *__init__.py

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

__all__ = ('some_name',)

Смотритетакже: 为什么“import *”不好?

4赞 prosti 6/18/2019 #10

这在 PEP8 中定义如下

全局变量名称

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

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

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