有人可以在Python中解释__all__吗?从 进口于 进口*

问题描述 投票:788回答:11

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

python syntax namespaces
11个回答
436
投票

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


2
投票

除了现有的答案,__all__不一定是列表。根据from package import *上的文档,如果定义,__all__必须是一系列字符串,这些字符串是由模块定义或导入的名称。所以你也可以使用一个元组来import statement一些内存和CPU周期。如果模块定义了一个公共名称,请不要忘记逗号:

__all__


2
投票

这在PEP8 save中有明确定义:

全局变量名称

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

设计用于通过__all__ = ('some_name',)使用的模块应该使用here机制来防止输出全局变量,或者使用旧的约定这样的全局变量加上下划线(您可能希望这样做以表明这些全局变量是“非公共模块”)。

PEP8提供Python代码的编码约定,包括主Python分发中的标准库。你遵循的越多,你就越接近原点。


830
投票

链接到,但未在此明确提及,正是在使用__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>导入。


162
投票

我只是简单地添加这个:

所有其他答案都参考模块。最初的问题在qazxsw poi文件中明确提到了qazxsw poi,所以这是关于python包的。

一般来说,当使用__all__语句的__init__.py变体时,__all__才会发挥作用。这适用于包和模块。

其他答案中解释了模块的行为。包的确切行为详细描述了from xxx import *

简而言之,封装级别的import与模块大致相同,只是它处理包中的模块(与在模块中指定名称相反)。所以here指定当我们使用__all__时应加载并导入当前命名空间的所有模块。

最大的区别是,当你在包的__all__中省略from package import *的声明时,语句__all__根本不会导入任何内容(文档中有例外说明,请参见上面的链接)。

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


148
投票

在Python中解释__all__?

我一直看到变量from package import *设置在不同的__all__文件中。

这是做什么的?

What does __all__ do?

它从模块声明语义上的“公共”名称。如果在__init__.py中有一个名字,那么用户应该使用它,并且他们可以期望它不会改变。

它还会产生程序化影响:

__all__

__all__在一个模块中,例如import *

__all__

意味着当你从模块中module.py时,只导入__all__ = ['foo', 'Bar'] 中的那些名字:

import *

文档工具

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

from module import * # imports foo and Bar makes a directory a Python package

来自__all__

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

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

所以__init__.py可以宣布__all__为一个包。

Managing an API:

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

__all__

在你写的__init__.py

 package/
   |-__init__.py # makes directory a Python package
   |-module_1.py
   |-module_2.py

__init__.py你有:

from module_1 import *
from module_2 import *

module_1你有:

__all__ = ['foo',]

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

module_2

并且它们不会包含您在创建模块时使用的所有其他名称,这些名称会使__all__ = ['Bar',] 命名空间变得混乱。

import package package.foo() package.Bar() in package

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

__all__

在每个__init__.py你声明一个 package/ |-__init__.py |-module_1/ | |-__init__.py | |-foo_implementation.py |-module_2/ |-__init__.py |-Bar_implementation.py ,例如在module_1中:

__init__.py

和module_2的__all__

from foo_implementation import *
__all__ = ['foo']

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

from Bar_implementation import *
__all__ = ['Bar']

如果您还没准备好在顶级API中发布__init__.py,那么在您的顶级from Bar_implementation import * from Baz_implementation import * __all__ = ['Bar', 'Baz'] 中,您可以:

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

Prefixing Baz versus from module_1 import * from module_2 import * __all__ = ['foo', 'Bar', 'Baz'] :

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

_

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

我个人在开发生命周期的早期就为模块编写了一个_,以便其他可能使用我的代码的人知道他们应该使用什么而不是使用它们。

标准库中的大多数软件包也使用_

When avoiding __all__ makes sense

在以下情况下坚持使用__all__前缀约代替__all__是有道理的:

  • 您仍处于早期开发模式且没有用户,并且不断调整您的API。
  • 也许您确实拥有用户,但您拥有覆盖API的单元测试,并且您仍在积极地添加API并在开发中进行调整。

An _ decorator

使用__all__的缺点是你必须编写两次导出的函数和类的名称 - 并且信息与定义分开。我们可以使用装饰器来解决这个问题。

我从David Beazley的包装谈话中得到了这样一个出口装饰的想法。这种实现似乎在CPython的传统导入器中运行良好。如果你有一个特殊的导入钩子或系统,我不保证,但如果你采用它,退出是相当简单的 - 你只需要手动将名称添加回export

因此,在例如实用程序库中,您将定义装饰器:

__all__

然后,你要定义一个__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

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

__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() 进行API配置也会起作用:

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

$ python run.py
main

85
投票

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

module1.朋友

import *

module2.朋友

$ 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

$ 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'

我在所有模块中声明了a = "A" b = "B" c = "C" ,以及强调内部细节,这些在使用您以前从未使用过的实时解释器会话时非常有用。


50
投票

来自__all__ = ['a', 'b'] a = "A" b = "B" c = "C"

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


40
投票

__all__ customizes the asterisk in __all__

__all__ customizes the asterisk in from <module> import *


__all__是一个用于导入的from <package> import *文件。

module是一个带有.py文件的目录。包通常包含模块。


MODULES

package

__init__.py让人类知道模块的“公共”特征。[""" cheese.py - an example module """ __all__ = ['swiss', 'cheddar'] swiss = 4.99 cheddar = 3.99 gouda = 10.99 ]此外,pydoc识别它们。[__all__]

来自模块导入*

看看如何将@AaronHall@Longpoke带入本地命名空间,而不是swiss

cheddar

如果没有gouda,任何符号(不以下划线开头)都可用。


Imports without >>> 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 are not affected by __all__


导入模块

*

来自模块导入名称

__all__

导入模块为localname

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

PACKAGES

在包的>>> 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... ] 中定义的对象:

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


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

[__init__.py,@ ToolmakerSteve]


9
投票

Short answer

__init__.py影响PEP 8声明。

Long answer

考虑这个例子:

__all__

from <module> import *

  • (隐式)如果我们不定义foo ├── bar.py └── __init__.py ,那么foo/__init__.py将只导入__all__中定义的名称。
  • (明确)如果我们定义from foo import *,那么foo/__init__.py将不会导入任何内容。
  • (明确)如果我们定义__all__ = [],那么from foo import *将只导入这些名称。

请注意,在隐式的情况下,python不会导入以__all__ = [ <name1>, ... ]开头的名称。但是,您可以使用from foo import *强制导入此类名称。

您可以查看Python文档_


8
投票

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

以下是__all__的相关摘录:

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

__all__使用类似的措辞,但它也清楚地表明,当__all__缺席时,导入的名称不是公共API的一部分:

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

[...]

应始终将导入的名称视为实现细节。其他模块不能依赖于对这些导入名称的间接访问,除非它们是包含模块的API的明确记录部分,例如__all__或从子模块公开功能的包的__all__模块。

此外,正如其他答案中所指出的,os.path用于启用__init__

import语句使用以下约定:如果包的__all__代码定义名为wildcard importing for packages的列表,则它将被视为遇到__init__.py时应导入的模块名称列表。

© www.soinside.com 2019 - 2024. All rights reserved.