__all__在Python中是什么意思?

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

520372 次浏览

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

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

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

__all__ = ['bar', 'baz']
waz = 5bar = 10def baz(): return 'baz'

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

from foo import *
print(bar)print(baz)
# The following will trigger an exception, as "waz" is not exported by the moduleprint(waz)

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

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

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

它还改变了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:
NAMEmodule1
FILEmodule1.py
DATAa = 'A'b = 'B'c = 'C'

$pydoc模块

Help on module module2:
NAMEmodule2
FILEmodule2.py
DATA__all__ = ['a', 'b']a = 'A'b = 'B'

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

我只是添加这个是准确的:

所有其他答案都指模块。最初的问题在__init__.py文件中明确提到了__all__,所以这是关于python的。

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

模块的行为在其他答案中解释。包的确切行为详细描述这里

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

最大的区别是,当你省略在包的__init__.py中声明__all__时,语句from package import *根本不会导入任何东西(留档中解释了例外情况,请参阅上面的链接)。

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

解释所有在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 = Nonedef foo(): pass""")
package_module_2 = package / 'module_2.py'package_module_2.write_text("""__all__ = ['Bar']imp_detail1 = imp_detail2 = imp_detail3 = Noneclass Bar: pass""")

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

import packagepackage.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')

为每个声明__all__的子包创建__init__.py

(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添加内容。如果您想向子包添加新名称,只需更新__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__'sfrom .module_2 import *       # in the __init__.py's__all__ = ['foo', 'Bar']     # further constraining the names advertised

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

import packagepackage.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.pyUSALLCAPS = "all caps"us_snake_case = "snake_case"_us_non_public = "shouldn't import"$ pythonPython 3.10.0 (default, Oct  4 2021, 17:55:55) [GCC 10.3.0] on linuxType "help", "copyright", "credits" or "license" for more information.>>> from us import *>>> dir()['USALLCAPS', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'us_snake_case']

你肯定是可以依赖于这种机制。Python标准库中的一些包,事实上,依赖于这种机制,但为了这样做,它们对它们的导入进行了别名,例如,在#0中:

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.pyfrom lib import export__all__ = [] # optional - we create a list if __all__ is not there.
@exportdef foo(): pass
@exportdef bar():'bar'
def main():print('main')
if __name__ == '__main__':main()

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

$ cat > run.pyimport mainmain.main()
$ python run.pymain

使用import *的API配置也可以工作:

$ cat > run.pyfrom main import *foo()bar()main() # expected to error here, not exported
$ python run.pyTraceback (most recent call last):File "run.py", line 4, in <module>main() # expected to error here, not exportedNameError: name 'main' is not defined

__all__自定义* infrom <module> import *#0.


模块是要导入的.py文件。

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


模块

""" cheese.py - an example module """
__all__ = ['swiss', 'cheddar']
swiss = 4.99cheddar = 3.99gouda = 10.99

__all__让人类知道模块的“公共”特征。[@亚伦·霍尔]此外,pydoc可以识别它们。[@龙戳客

from模块导入*

查看如何将swisscheddar引入本地命名空间,而不是gouda

>>> from cheese import *>>> swiss, cheddar(4.99, 3.99)>>> goudaTraceback (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)

包裹

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

以下是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...
]

默认情况没有#0的星号表示一个包很复杂,因为显而易见的行为会很昂贵:使用文件系统搜索包中的所有模块。相反,在我阅读文档时,只导入__init__.py中定义的对象:

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


最后,对于堆栈溢出的答案,教授和各地的男性说教者来说,一个受人尊敬的传统是首先提出问题的责备bon mot

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

[PEP 8,@ToolmakerSteve]

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

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

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

PEP 8使用了类似的措辞,尽管它也清楚地表明,当没有__all__时,导入的名称不是公共API的一部分:

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

[…]

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

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

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

简短的回答

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

__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__不必是列表。根据#1语句上的留档,如果定义了,__all__必须是串序列,它们是模块定义或导入的名称。因此,您不妨使用元组来保存一些内存和CPU周期。如果模块定义了单个公共名称,请不要忘记逗号:

__all__ = ('some_name',)

另见为什么“导入”不好?

这在PEP8这里中定义:

全局变量名

(让我们希望这些变量仅用于一个模块内。)约定与函数的约定大致相同。

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

PEP8为Python代码提供了编码约定,包括Python主发行版中的标准库。