我越来越多地使用Python,并且我一直__all__
在不同的__init__.py
文件中看到变量集.有人可以解释这是做什么的吗?
链接到,但未在此明确提及,正好在何时__all__
使用.它是一个字符串列表,用于定义模块from
上使用时模块中将导出的符号.
例如,以下代码foo.py
显式导出符号bar
和baz
:
__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/3.5/tutorial/modules.html#importing-from-a-package
注意: 仅__all__
影响from
行为.未提及的成员__all__
仍可从模块外部访问,并可以导入from
.
它是该模块的公共对象列表,由解释import *
.它会覆盖隐藏以下划线开头的所有内容的默认设置.
怎么在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__
一个包.
包通常由可以相互导入的模块组成,但必须与__init__.py
文件捆绑在一起.该文件是使目录成为实际Python包的原因.例如,假设您有以下内容:
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',]
在module_2
你有:
__all__ = ['Bar',]
现在,您已经提供了一个完整的api,其他人可以在导入您的包时使用,如下所示:
import package package.foo() package.Bar()
并且它们不会包含您在创建模块时使用的所有其他名称package
.
__all__
在 __init__.py
经过更多的工作,也许你已经确定模块太大了,需要拆分.所以你做了以下事情:
package/ |-__init__.py |-module_1/ | |-__init__.py | |-foo_implementation.py |-module_2/ |-__init__.py |-Bar_implementation.py
并在每个中__init__.py
声明一个__all__
,例如在module_1中:
from foo_implementation import * __all__ = ['foo']
和module_2的__init__.py
:
from Bar_implementation import * __all__ = ['Bar']
您可以轻松地向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将导出所有不以a开头的名称_
.你当然可以依靠这种机制.事实上,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()
无论是作为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, inmain() # expected to error here, not exported NameError: name 'main' is not defined
我只是简单地添加这个:
所有其他答案都参考模块.原始问题__all__
在__init__.py
文件中明确提到,所以这是关于python 包的.
通常,__all__
只有在使用语句的from xxx import *
变体时才会发挥作用import
.这适用于包和模块.
其他答案中解释了模块的行为.这里详细描述了包的确切行为.
简而言之,__all__
在包级别上与模块大致相同,除了它处理包中的模块 (与在模块中指定名称相反).因此,__all__
指定在我们使用时应加载并导入当前命名空间的所有模块from package import *
.
最大的区别是,当你忽略的声明__all__
在包的__init__.py
,声明from package import *
将不导入所有东西(有例外的文档中解释,见上面的链接).
另一方面,如果__all__
在模块中省略,"已加星标的导入"将导入模块中定义的所有名称(不以下划线开头).
它还会改变pydoc将显示的内容:
module1.py
a = "A" b = "B" c = "C"
module2.py
__all__ = ['a', 'b'] a = "A" b = "B" c = "C"
$ 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__
在所有模块中声明,以及强调内部细节,这些在使用您以前从未使用过的实时解释器会话时真的很有帮助.
来自(非官方)Python参考Wiki:
模块定义的公共名称是通过检查模块命名空间的命名空间来确定的
__all__
; 如果已定义,则它必须是一个字符串序列,这些字符串是由该模块定义或导入的名称.给出的名称__all__
都被认为是公开的,并且必须存在.如果__all__
未定义,则公共名称集包括在模块命名空间中找到的所有名称,这些名称不以下划线字符("_")开头.__all__
应该包含整个公共API.它旨在避免意外导出不属于API的项目(例如在模块中导入和使用的库模块).
__all__
自定义星号的*
from import *
自定义星号的__all__
一个模块是*
意味着要导入的文件.
一包是一个目录from
文件.包通常包含模块.
""" cheese.py - an example module """ __all__ = ['swiss', 'cheddar'] swiss = 4.99 cheddar = 3.99 gouda = 10.99
.py
让人类知道模块的"公共"特征.[ @AaronHall ] 此外,pydoc识别它们.[ @Longpoke ]
了解如何__init__.py
以及如何__all__
将其引入本地命名空间,但不是swiss
:
>>> from cheese import * >>> swiss, cheddar (4.99, 3.99) >>> gouda Traceback (most recent call last): File "", line 1, in NameError: name 'gouda' is not defined
没有cheddar
,任何符号(不以下划线开头)都可用.
gouda
不受影响__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)
在包的*
文件中是一个包含公共模块或其他对象名称的字符串列表.这些功能可用于通配符导入.与模块一样,从包中自定义通配符导入时.[ @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... ]
应该避免使用通配符......因为它们会混淆读者和许多自动化工具.
[ PEP 8,@ ToolmakerSteve]
__all__
影响from
陈述.
考虑这个例子:
foo ??? bar.py ??? __init__.py
在foo/__init__.py
:
(隐式)如果我们没有定义__all__
,那么from foo import *
只会导入在中定义的名称foo/__init__.py
.
(明确)如果我们定义__all__ = []
,则from foo import *
不会导入任何内容.
(明确)如果我们定义__all__ = [
,那么from foo import *
只会导入这些名称.
请注意,在隐式的情况下,python不会导入以_
.开头的名称.但是,您可以使用强制导入此类名称__all__
.
您可以在此处查看Python文档.
__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 *
遇到时应导入的模块名称列表.