有人可以在Python中解释__all__吗?

var*_*kin 873 python syntax namespaces

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

Ale*_*mas 883

链接到,但未在此明确提及,正好在何时__all__使用.它是一个字符串列表,用于定义模块from <module> import *上使用时模块中将导出的符号.

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

__all__ = ['bar', 'baz']

waz = 5
bar = 10
def baz(): return 'baz'
Run Code Online (Sandbox Code Playgroud)

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

from foo import *

print(bar)
print(baz)

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

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

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

注意:__all__影响from <module> import *行为.未提及的成员__all__仍可从模块外部访问,并可以导入from <module> import <member>.

  • @JulioCezarSilva 有点偏离主题,但值得注意的是,对于类和函数,您可以使用 `__name__` 属性 (20认同)
  • 我觉得令人困惑的是,直到今天,还没有一种方法可以通过直接引用函数/对象来填充 __all__` 。相反,我们必须记下他们的名字,并在名字发生变化时单独更正。对于活动代码库来说似乎很容易出现错误。 (12认同)
  • 目的是说明符号已导出。是否执行该函数是次要的。 (7认同)
  • @ArtificiallyIntelligence 来自 PEP 8:“通配符导入有一个合理的用例,即重新发布内部接口作为公共 API 的一部分。” 此外,问题和答案是关于 __all__ 特殊变量的使用,并且不支持通配符导入。这就是 __all__ 的用法。 (4认同)
  • @JohnCole baz 是函数对象,baz() 将运行该函数对象 (2认同)
  • @phsyron 虽然游览建议似乎合法,但它不需要已经导入模块吗?我问这个问题是因为通过这种方式填充 `__all__` 我仍然会收到有关来自 mypy 的隐式导入的警告,而通过显式键入则不会收到警告。 (2认同)

Jim*_*mmy 478

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

  • 以下划线开头的对象,或者如果存在`__all__`则在`__all__`中没有提到的对象,并不是完全隐藏的; 如果你知道他们的名字,他们可以被正常地看到和访问.只有在"进口*"的情况下,无论如何不推荐这种区别具有任何重量. (134认同)
  • @BrandonRhodes:这也不完全正确:建议只导入你知道为`import*`设计的模块(例如`tk`).如果是这种情况,一个很好的提示是在模块的代码中存在`__all__`或以下划线开头的名称. (24认同)
  • 公共和内部接口 - https://www.python.org/dev/peps/pep-0008/#id50,为了更好地支持内省,模块应该使用\ _\_ all\_ _\_显式声明其公共API中的名称_属性.将\ _ _ _ all\_ _ _设置为空列表表示该模块没有公共API. (8认同)
  • 总结一下:如果你有`__all__`,`import *`将导入`__all__`中的所有内容,否则,它将导入不以下划线开头的所有内容。 (5认同)
  • 我不确定如果今天(或者 2012 年)发布了 `tk`,推荐的做法是使用 `from tk import *`。我认为这种做法被接受是由于惯性,而不是有意设计。 (4认同)

Aar*_*all 170

在Python中解释__all__?

我一直__all__在不同的__init__.py文件中看到变量集.

这是做什么的?

怎么__all__办?

它从模块声明语义上的"公共"名称.如果有一个名称__all__,用户应该使用它,他们可以期望它不会改变.

它还会产生程序化影响:

import *

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

__all__ = ['foo', 'Bar']
Run Code Online (Sandbox Code Playgroud)

表示当您import *从模块中导出时,只__all__导入其中的名称:

from module import *               # imports foo and Bar
Run Code Online (Sandbox Code Playgroud)

文档工具

文档和代码自动完成工具(实际上应该)也可以检查__all__以确定模块中可用的名称.

__init__.py 使目录成为Python包

来自文档:

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

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

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

管理API:

包通常由可以相互导入的模块组成,但必须与__init__.py文件捆绑在一起.该文件是使目录成为实际Python包的原因.例如,假设您有以下内容:

 package/
   |-__init__.py # makes directory a Python package
   |-module_1.py
   |-module_2.py
Run Code Online (Sandbox Code Playgroud)

__init__.py你写的:

from module_1 import *
from module_2 import *
Run Code Online (Sandbox Code Playgroud)

module_1你有:

__all__ = ['foo',]
Run Code Online (Sandbox Code Playgroud)

module_2你有:

__all__ = ['Bar',]
Run Code Online (Sandbox Code Playgroud)

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

import package
package.foo()
package.Bar()
Run Code Online (Sandbox Code Playgroud)

并且它们不会包含您在创建模块时使用的所有其他名称package.

__all____init__.py

经过更多的工作,也许你已经确定模块太大了,需要拆分.所以你做了以下事情:

 package/
   |-__init__.py
   |-module_1/
   |  |-__init__.py
   |  |-foo_implementation.py
   |-module_2/
      |-__init__.py
      |-Bar_implementation.py
Run Code Online (Sandbox Code Playgroud)

并在每个中__init__.py声明一个__all__,例如在module_1中:

from foo_implementation import *
__all__ = ['foo']
Run Code Online (Sandbox Code Playgroud)

和module_2的__init__.py:

from Bar_implementation import *
__all__ = ['Bar']
Run Code Online (Sandbox Code Playgroud)

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

from Bar_implementation import *
from Baz_implementation import *
__all__ = ['Bar', 'Baz']
Run Code Online (Sandbox Code Playgroud)

如果您还没有准备好在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
Run Code Online (Sandbox Code Playgroud)

如果您的用户了解其可用性Baz,他们可以使用它:

import package
package.Baz()
Run Code Online (Sandbox Code Playgroud)

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

您可以在以后Baz准备黄金时间时更改它:

from module_1 import *
from module_2 import *
__all__ = ['foo', 'Bar', 'Baz']
Run Code Online (Sandbox Code Playgroud)

前缀___all__:

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

import os as _os, sys as _sys
Run Code Online (Sandbox Code Playgroud)

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

我个人__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
Run Code Online (Sandbox Code Playgroud)

然后,你要定义一个__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()
Run Code Online (Sandbox Code Playgroud)

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

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

$ python run.py
main
Run Code Online (Sandbox Code Playgroud)

并且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
Run Code Online (Sandbox Code Playgroud)

  • 对于帮助一个相对较新的python开发人员了解使用`__init __.py`导入模块/包的过程以及使用`__all__`,我一直是最有帮助的答案. (8认同)

Mar*_*ner 169

我只是简单地添加这个:

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

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

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

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

最大的区别是,当你忽略的声明__all__在包的__init__.py,声明from package import *将不导入所有东西(有例外的文档中解释,见上面的链接).

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

  • `from package import*`仍将导入`__init __.py`中定义的所有内容,即使没有`all`.重要的区别是没有`__all__`它不会自动导入包目录中定义的任何模块. (25认同)

L̲̳*_*̲̳̳ 85

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

module1.py

a = "A"
b = "B"
c = "C"
Run Code Online (Sandbox Code Playgroud)

module2.py

__all__ = ['a', 'b']

a = "A"
b = "B"
c = "C"
Run Code Online (Sandbox Code Playgroud)

$ pydoc module1

Help on module module1:

NAME
    module1

FILE
    module1.py

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

$ pydoc module2

Help on module module2:

NAME
    module2

FILE
    module2.py

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

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


ang*_*son 50

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

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


Bob*_*ein 49

__all__自定义星号*

from <module> import *自定义星号__all__


一个模块*意味着要导入的文件.

是一个目录from <package> import *文件.包通常包含模块.


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

__all__ = ['swiss', 'cheddar']

swiss = 4.99
cheddar = 3.99
gouda = 10.99
Run Code Online (Sandbox Code Playgroud)

.py让人类知道模块的"公共"特征.[ @AaronHall ] 此外,pydoc识别它们.[ @Longpoke ]

来自模块导入*

了解如何__init__.py以及如何__all__将其引入本地命名空间,但不是swiss:

>>> 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
Run Code Online (Sandbox Code Playgroud)

没有cheddar,任何符号(不以下划线开头)都可用.


没有进口gouda不受影响__all__


导入模块

>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)
Run Code Online (Sandbox Code Playgroud)

来自模块导入名称

>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)
Run Code Online (Sandbox Code Playgroud)

导入模块localname

>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)
Run Code Online (Sandbox Code Playgroud)

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

以下是Python MySQL Connector 的摘录__all__:

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

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

    # Error handling
    'Error', 'Warning',

    ...etc...

    ]
Run Code Online (Sandbox Code Playgroud)

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

[ PEP 8,@ ToolmakerSteve]

  • 我真的很喜欢这个答案,但是我缺少关于&lt;from&gt; &lt;package&gt; import *`在__init__.py中没有__all__的默认行为的信息,即**不导入任何模块**。 (2认同)

Cyk*_*ker 9

简短的回答

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

答案很长

考虑这个例子:

foo
??? bar.py
??? __init__.py
Run Code Online (Sandbox Code Playgroud)

foo/__init__.py:

  • (隐式)如果我们没有定义__all__,那么from foo import *只会导入在中定义的名称foo/__init__.py.

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

  • (明确)如果我们定义__all__ = [ <name1>, ... ],那么from foo import *只会导入这些名称.

请注意,在隐式的情况下,python不会导入以_.开头的名称.但是,您可以使用强制导入此类名称__all__.

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


Eug*_*ash 9

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

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

from foo import *
Run Code Online (Sandbox Code Playgroud)

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

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

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

__all__ = ('some_name',)
Run Code Online (Sandbox Code Playgroud)

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


Mih*_*otă 7

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