Python中的__all__是什么意思?

1672

我在__init__.py文件中看到了__all__。它是用来做什么的?

10个回答

1520

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

例如,在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>导入。

6
@JohnCole baz是一个函数对象,baz()会执行这个函数对象。 - Bhanu Tez
24
目标是说明符号已经被导出,执行函数与否并不重要。 - Alec Thomas
73
直到今天,我觉得令人困惑的是,没有一种方式可以通过直接引用函数/对象来自动填充__all__。相反,我们必须手动输入它们的名称并在每次名称更改时单独进行更正。对于活跃的代码库来说,这似乎非常容易出错。 - Julio Cezar Silva
20
值得注意的是,针对类和函数,您可以使用__name__属性。 - phsyron
4
从PEP 8中可以看出,通配符导入有一个可辩护的用例,即将内部接口作为公共API的一部分重新发布。此外,问题和答案涉及__all__特殊变量的使用,并不支持通配符导入。那只是__all__的用法。 - Xbox One
显示剩余6条评论

849

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


190
以一个下划线开头的对象,或者没有在__all__中提到的对象(如果__all__存在),并不是完全隐藏的;如果你知道它们的名称,它们可以被正常地看到和访问。只有在“import *”的情况下才会有区别,但这种方式并不推荐使用。 - Brandon Rhodes
35
@BrandonRhodes:这也不完全正确:建议只导入你知道被设计为 import * 的模块(比如例如 tk)。如果是这种情况,一个很好的提示就是模块代码中存在 __all__ 或以下划线开头的名称。 - flying sheep
22
公共和内部接口 - https://www.python.org/dev/peps/pep-0008/#id50,为了更好地支持内省(introspection),模块应该使用“\_\_all\_\_”属性来明确声明其公共API中的名称。将“\_\_all\_\_”设置为空列表表示该模块没有公共API。 - debug
5
我不确定如果今天(甚至是在2012年)发布tk,是否推荐使用from tk import *。我认为这种做法是出于惯性而被接受的,而不是经过有意的设计。 - chepner
9
总结一下:如果你有__all__,那么import *会导入__all__中的所有内容;否则,它将导入所有不以下划线开头的内容。 - JP Zhang
显示剩余2条评论

586

解释Python中的__all__是什么?

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

这是做什么用的?

__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 文件。这样做是为了防止具有常见名称(例如 string)的目录无意中隐藏在模块搜索路径后面的有效模块中。

在最简单的情况下,__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名称空间中。

__init__.py 中的 __all__

经过更多的工作,也许你已经决定这些模块太大了(比如成千上万行?),需要进行拆分。因此,你进行以下操作:

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__
(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,您可以在顶级__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 会在使用 import * 导入时导出所有不以 _ 开头的名称。如下所示,在 shell 会话中演示,import * 不会从 us.py 模块中引入 _us_non_public 名称:

$ 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__有意义时
当:
您仍处于早期开发模式中,并且没有用户,并且正在不断调整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()

这个功能无论是作为主程序运行还是被其他函数导入都可以正常工作。

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

$ python run.py
main

使用import *进行API配置也可以:

$ 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
交叉引用:我在这个问题的这个CW答案中提到了你的装饰器,该问题是关于如何编写@export装饰器的。 - MvG
71
这是我见过的最有帮助的答案之一,对于一个相对新手的Python开发者来说,它能够帮助他理解如何使用__init__.py导入模块/包以及__all__的用法。 - Brett Reinhard
这对我很有帮助。我的问题是,我想要导入的子模块都是生成的文件,其中包含很多杂乱的符号,我想剥离它们,而不必手动确保 __all__ 是正确的。 - Mike C
1
我澄清了:“默认情况下,Python将在使用import *导入时导出所有不以_开头的名称。” 我添加了一个shell会话来演示这一点,如现在所描述的那样工作。 - Russia Must Remove Putin
1
@ruslaniv 对我来说,该语句的目的是为了确保包的__all__定义与使用from module1 import *from module2 import *导入的内容完全匹配,同时不包括作为其导入部分而存在于包中的任何其他符号,如果有人执行from package import *。例如,如果包包含额外的测试脚本或一个不应直接访问的第三个子模块,那么除非显式导入,否则这些文件中的符号将不会被导入。 - nigh_anxiety
显示剩余6条评论

198

我只是为了更精确而添加这个:

所有其他答案都涉及到模块。原问题明确提到__init__.py文件中的__all__,因此这涉及到Python的

通常情况下,只有在使用from xxx import *变体的import语句时,__all__才会发挥作用。这适用于包和模块。

对于模块的行为在其他答案中已经解释过了。有关包的确切行为在此处详细描述。

简而言之,在包级别上,__all__所做的事情与模块类似,只不过它处理的是包中的模块(与指定模块内的名称相反)。因此,__all__指定了所有要加载和导入到当前命名空间中的模块,当我们使用from package import *时。

最大的区别在于,如果您在包的__init__.py中省略了__all__声明,则from package import *语句将不会导入任何内容(有例外情况,详见文档,参见上面的链接)。

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


32
即使没有定义 allfrom package import * 仍会导入 __init__.py 中定义的所有内容。重要的区别在于,如果没有 __all__,它不会自动导入包目录中定义的任何模块。 - Nikratio
1
当__all__包含[foo, bar],并且在test.py文件中我们使用以下代码:from package import *,那么foo和bar会被导入到test.py的本地命名空间中还是它们自己的命名空间中? - variable
这个答案是错误的。实际上,所指的文档中的“模块名称”是指模块命名空间中的名称,而不是包中模块的名称(请参阅https://github.com/python/cpython/issues/106556)。 - undefined

97

它还会改变pydoc显示的内容:

module1.py

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

module2.py

__all__ = ['a', 'b']

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

$ pydoc module1

模块 module1 的帮助文档:

名称
    module1

文件
    module1.py

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

$ pydoc module2

模块 module2 的帮助文档:

名称
    module2

文件
    module2.py

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

我在所有的模块中都声明了__all__,以及下划线内部细节,在实时解释器会话中使用从未使用过的东西时,这些确实非常有用。


92

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

模块导入*

看看如何将swisscheddar带入本地命名空间,但不包括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 文件中的 __all__ 是一个字符串列表,列出了公共模块或其他对象的名称。这些特性可用于通配符导入。与模块一样,__all__ 自定义通配符导入包时的 *[@MartinStettner]

以下是 Python MySQL Connector__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中定义的对象被导入:

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


最后,对于Stack Overflow答案、教授和男性解释者而言,一种受人尊敬的传统是责备提出问题的人:

应该避免使用通配符导入...因为它们会使读者和许多自动化工具感到困惑。

[PEP 8, @ToolmakerSteve]


2
我非常喜欢这个答案,但是我错过了关于在__init__.py中没有__all__的情况下from <package> import *的默认行为,即不导入任何模块的信息。 - radzak
感谢@Jatimir,我尽力在不运行实验的情况下澄清了问题。我几乎想说这种情况(没有全部打包的星号)的行为与__init__.py是一个模块一样。但我不确定这是否准确,特别是是否排除了下划线前缀的对象。此外,我更清楚地区分了“模块”和“包”两个部分。你有什么想法? - Bob Stein

19

简短回答

__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 文档 这里


15

__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 * 时应该导入的模块名称列表。


12

__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__不一定是一个列表。根据import语句的文档,如果定义了__all__,则__all__必须是模块定义或导入的名称的字符串序列。因此,您也可以使用元组来节省一些内存和CPU周期。只是不要忘记在模块定义单个公共名称的情况下加上逗号:
__all__ = ('some_name',)

参见 为什么“import *”是不好的?


4
这是在PEP8中定义的,这里有详细说明:
全局变量名 (希望这些变量只用于一个模块内。)惯例与函数的相同。 设计为通过“from M import *”使用的模块应使用__all__机制防止导出全局变量,或者使用旧约定,用下划线前缀这样的全局变量(您可能想要这样做来指示这些全局变量是“模块非公共的”)。
PEP8为主Python发行版中包含的Python代码提供了编码约定。遵循得越多,就越接近原始意图。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接