有人可以用Python解释__all__吗?


982

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

Answers:


565

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


146
以下划线开头或__all__如果__all__存在则未提及的对象不会被完全隐藏。如果您知道他们的名字,则可以正常看到和访问它们。仅在“ import *”(无论如何不建议使用)的情况下,区别才有意义。
布兰登·罗兹

28
@BrandonRhodes:也不完全正确:建议仅导入您知道要为其设计的模块import *(例如tk)。如果是这种情况,一个很好的提示是__all__模块代码中是否存在或以下划线开头的名称。
飞羊

12
公共和内部接口-python.org/dev/peps/pep-0008/#id50,为了更好地支持自省,模块应使用__all__属性在其公共API中显式声明名称。将__all__设置为空列表表示该模块没有公共API。
调试

我不确定如果tk今天(或什至2012年)发行了,建议的做法是使用from tk import *。我认为这种做法是由于惯性而不是故意设计而被接受的。
chepner

正如布兰登·罗德斯(BrandonRhodes)指出的那样,这确实是不正确的
duhaime '19

946

链接到(但未在此处明确提及的)确切是何时__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>


1
我们不应该将baz打印为print(baz())吗?
约翰·科尔

@JohnCole baz是函数对象,baz()将运行该函数对象
Bhanu Tez

完全是@BhanuTez。所以print(baz)打印类似<function baz at 0x7f32bc363c10>print(baz())打印baz
John Cole

223

用Python解释__all__吗?

我不断__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__每个子包的子包创建:

(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']

如果您还没有准备好在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

使用_约定可能更优雅,因为它消除了再次命名名称的麻烦。但这增加了导入的冗余(如果您有很多导入的话),很容易忘记一贯地执行此操作-最后要做的就是无限期地支持您打算仅作为实现细节的内容,只是因为您_在命名函数时忘了给加上前缀。

我个人__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()

无论是作为主程序运行还是由其他函数导入,此方法都可以正常工作。

$ 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
交叉引用:我在此CW回答中提到了您的装饰器,装饰器如何编写@export装饰器。
MvG

13
就帮助相对较新的python开发人员了解__init__.py使用/导入模块/软件包的过程以及使用它们的方式,这是我所见过的最有用的答案__all__
Brett Reinhard

这对我有很大帮助。我的问题是,我要导入的子模块都是生成的文件,这些文件的符号很多,我想删除这些符号,而不必手动确保其__all__正确。
Mike C

@MikeC,那么也许您也应该生成您的代码__all__-但是我会说您的API不稳定...这将是一些全面的验收测试的依据。
亚伦·霍尔

@AaronHall“它们不会具有所有其他名称……会使软件包名称空间混乱”,但是它们具有名称module_1module_2;是否确定包括一个明确del module_1__init__.py?我认为这是值得的吗?
Mike C

176

我只是添加这是为了精确:

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

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

模块的行为在其他答案中进行了说明。包的确切行为在此处详细描述。

简而言之,__all__在程序包级别,它与模块大致相同,只是它处理程序包中的模块 (与在模块中指定名称相反)。因此__all__指定当我们使用时应加载并导入到当前名称空间中的所有模块from package import *

最大的区别是,当你忽略的声明__all__在包的__init__.py,该声明from package import *将完全不进口任何东西(有例外的文档中解释,见上面的链接)。

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


29
from package import *__init__.py即使没有定义,也仍将导入其中定义的所有内容all。重要的区别在于,如果没有__all__它,将不会自动导入包目录中定义的任何模块。
Nikratio

如果所有文件包含[foo,bar]并在test.py文件中包含以下内容(如果我们使用):从包import *,那么foo和bar是否导入到test.py的本地名称空间中,还是在foo和bar自己的名称空间中?
变量

87

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

module1.py

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

module2.py

__all__ = ['a', 'b']

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

$ pydoc module1

关于模块module1的帮助:

名称
    模块1

文件
    module1.py

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

$ pydoc module2

关于模块module2的帮助:

名称
    模块2

文件
    module2.py

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

__all__在所有模块中都声明了内容,并强调了内部细节,这些内容在使用实时解释器会话中从未使用过的功能时确实有用。


54

__all__定制*from <module> import *

__all__定制*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文件中是带有公共模块或其他对象名称的字符串列表。这些功能可用于通配符导入。与模块一样,自定义从软件包导入通配符的时间。[ @MartinStettner ] __all____all__*

这是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。它还包括以前的import语句显式加载的包的所有子模块。


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

[ PEP 8,@ ToolmakerSteve]


2
我真的很喜欢这个答案,但是我没有关于默认行为的信息,from <package> import *而没有这样做__all____init__.py因为它不会导入任何模块
radzak

感谢@Jatimir,我尽可能不进行实验就澄清了。我几乎想说这种情况(对于软件包没有全部显示的星号)的行为就像__init__.py是module一样。但是我不确定这是否正确,特别是如果下划线前缀的对象被排除在外。另外,我更清楚地将模块和包装部分分开。你的想法?
鲍勃·斯坦因

49

摘自(非官方)Python参考维基

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


列出的链接已死。但是在vdocuments.net/…上找到了逐字记录,在 这里:dokumen.tips/documents/reference-567bab8d6118a.html
JayRizzo

8

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


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


5

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

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

from foo import *

*所有属性模块的要求foo(除了那些下划线开头)绑定为导入模块中的全局变量。当foo具有属性时__all__,属性的值是受此类型的绑定的名称的列表from语句。

If foo是一个,它__init__.py定义了一个名为__all__,则将其视为from foo import *遇到时应导入的子模块名称的列表。如果__all__未定义,则该语句将from foo import *导入包中定义的任何名称。这包括由定义的任何名称(以及明确加载的子模块)__init__.py

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

__all__ = ('some_name',)

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


1

这在PEP8定义在这里

全局变量名

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

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

PEP8为包含主要Python发行版中的标准库的Python代码提供了编码约定。您遵循的越多,您就越接近原始意图。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.