我越来越多地使用Python,并且我不断看到变量__all__
设置在不同的__init__.py
文件中。有人可以解释这是做什么的吗?
答案 0 :(得分:798)
链接到,但未在此明确提及,恰好是使用__all__
的时间。它是一个字符串列表,用于定义在模块上使用from <module> import *
时模块中将导出哪些符号。
例如,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/tutorial/modules.html#importing-from-a-package
注意: __all__
仅影响from <module> import *
行为。 __all__
中未提及的成员仍可从模块外部访问,可以使用from <module> import <member>
导入。
答案 1 :(得分:412)
这是该模块的公共对象列表,由import *
解释。它会覆盖隐藏以下划线开头的所有内容的默认设置。
答案 2 :(得分:156)
我只是准确地加上这个:
所有其他答案均指 modules 。最初的问题在__all__
文件中明确提到__init__.py
,所以这是关于python 包。
通常,__all__
仅在使用from xxx import *
语句的import
变体时才会发挥作用。这适用于包和模块。
其他答案中解释了模块的行为。详细描述了here包的确切行为。
简而言之,包级别上的__all__
与模块大致相同,只是它处理包中的模块(与在模块中指定名称相反) )。所以__all__
指定当我们使用from package import *
时应加载并导入当前命名空间的所有模块。
最大的区别在于,当您省略包__all__
中__init__.py
的声明时,语句from package import *
将根本不会导入任何内容(有关文档中解释的例外情况,请参阅上面的链接)。
另一方面,如果省略模块中的__all__
,“已加星标的导入”将导入模块中定义的所有名称(不以下划线开头)。
答案 3 :(得分:142)
在Python中解释__all__?
我一直看到变量
__all__
设置在不同的__init__.py
文件中。这是做什么的?
__all__
做什么?它宣称语义为&#34; public&#34;模块中的名称。如果__all__
中有名称,则需要用户使用它,并且可以期望它不会更改。
它还会产生程序化影响:
import *
__all__
,例如module.py
:
__all__ = ['foo', 'Bar']
表示当您从模块中import *
时,只会导入__all__
中的名称:
from module import * # imports foo and Bar
文档和代码自动完成工具(实际上应该)也可以检查__all__
以确定模块中显示的名称。
__init__.py
使目录成为Python包来自docs:
需要
__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&#39; __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']
如果您尚未准备好在顶级API中发布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)也不会通知他们。
您可以稍后在Baz
准备好黄金时间时更改:
from module_1 import *
from module_2 import *
__all__ = ['foo', 'Bar', 'Baz']
_
与__all__
:默认情况下,Python将导出所有不以_
开头的名称。你肯定可以依赖这种机制。事实上,Python标准库中的一些软件包 do 依赖于此,但为了这样做,它们为其导入设置了别名,例如,在ctypes/__init__.py
中:
import os as _os, sys as _sys
使用_
约定可以更加优雅,因为它消除了再次命名名称的冗余。但它增加了导入的冗余(如果你有很多)并且容易忘记一直这样做 - 而你想要的最后一件事是必须无限期地支持你想要的东西只是一个实现细节,只是因为在命名函数时忘了给_
添加前缀。
我个人在我的开发生命周期的早期为模块写了一个__all__
,以便其他可能使用我的代码的人知道他们应该使用什么而不是使用。
标准库中的大多数软件包也使用__all__
。
__all__
有意义在以下情况下坚持_
前缀约定代替__all__
是有道理的:
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
使用import *
进行API配置也会有效:
$ 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
答案 4 :(得分:85)
它还会改变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__
,并强调内部细节,这些在使用您以前从未使用过的实时解释器会话时非常有用。
答案 5 :(得分:50)
来自(An Unofficial) Python Reference Wiki:
模块定义的公共名称是通过检查模块名称为
__all__
的变量的命名空间来确定的;如果已定义,则它必须是一个字符串序列,这些字符串是由该模块定义或导入的名称。__all__
中给出的名称都被视为公开名称,并且必须存在。如果未定义__all__
,则公共名称集包括在模块命名空间中找到的所有名称,这些名称不以下划线字符(“_”)开头。__all__
应包含整个公共API。它旨在避免意外导出不属于API的项目(例如在模块中导入和使用的库模块)。
答案 6 :(得分:38)
__all__
在from <module> import *
__all__
在from <package> import *
module是要导入的.py
文件。
package是一个带有__init__.py
文件的目录。包通常包含模块。
""" cheese.py - an example module """
__all__ = ['swiss', 'cheddar']
swiss = 4.99
cheddar = 3.99
gouda = 10.99
__all__
让人们知道模块的“公共”特征。 [@AaronHall] 此外,pydoc会识别它们。 [@Longpoke]
了解swiss
和cheddar
如何进入本地命名空间,但不是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
的{{1}}文件中是一个包含公共模块或其他对象名称的字符串列表。这些功能可用于通配符导入。与模块一样,__all__
在从包中进行通配符导入时自定义__all__
。 [@MartinStettner]
以下摘自Python MySQL Connector *
:
__init__.py
默认情况asterisk with no __all__
for a package很复杂,因为明显的行为很昂贵:使用文件系统搜索包中的所有模块。相反,在我阅读文档时,只导入__all__ = [
'MySQLConnection', 'Connect', 'custom_error_exception',
# Some useful constants
'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
'HAVE_CEXT',
# Error handling
'Error', 'Warning',
...etc...
]
中定义的对象:
如果未定义
__init__.py
,则语句__all__
不将包from sound.effects import *
中的所有子模块导入当前名称空间;它只确保已导入包sound.effects
(可能在sound.effects
中运行任何初始化代码),然后导入包中定义的任何名称。这包括__init__.py
定义的任何名称(以及显式加载的子模块)。它还包括由以前的import语句显式加载的包的任何子模块。
应该避免使用通配符......因为他们[混淆]读者和许多自动化工具。
[PEP 8,@ ToolmakerSteve]
答案 7 :(得分:9)
__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文档here。
答案 8 :(得分:8)
__all__
用于记录Python模块的公共API。虽然它是可选的,但应使用__all__
。
以下是the Python language reference的相关摘录:
模块定义的公共名称是通过检查模块名称为
__all__
的变量的命名空间来确定的;如果已定义,则它必须是一个字符串序列,这些字符串是由该模块定义或导入的名称。__all__
中给出的名称都被视为公开名称,并且必须存在。如果未定义__all__
,则公共名称集包括在模块命名空间中找到的所有名称,这些名称不以下划线字符('_')开头。__all__
应包含整个公共API。它旨在避免意外导出不属于API的项目(例如在模块中导入和使用的库模块)。
PEP 8使用了类似的措辞,但它也清楚地表明,当__all__
不存在时,导入的名称不属于公共API:
为了更好地支持内省,模块应使用
__all__
属性在其公共API中明确声明名称。将__all__
设置为空列表表示该模块没有公共API。[...]
导入的名称应始终被视为实施细节。其他模块不能依赖于对这些导入名称的间接访问,除非它们是包含模块API的明确记录的部分,例如
os.path
或从子模块公开功能的包__init__
模块。
此外,正如其他答案所指出,__all__
用于启用wildcard importing for packages:
import语句使用以下约定:如果包的
__init__.py
代码定义了名为__all__
的列表,则它将被视为from package import *
时应导入的模块名称列表遇到了。
答案 9 :(得分:2)
除了现有答案外,-Unique
不必是列表。根据{{3}}上的文档(如果已定义),__all__
必须是字符串序列,它们是模块定义或导入的名称。因此,您也可以使用元组来import
statement占用一些内存和CPU周期。只是不要忘记逗号,以防模块定义了一个公共名称:
__all__
答案 10 :(得分:1)
这在PEP8 here中已明确定义:
全局变量名称
(希望这些变量只能在一个模块内使用。)这些约定与函数的约定大致相同。
旨在通过
from M import *
使用的模块应使用__all__
机制来防止导出全局变量,或使用较早的约定在此类全局变量前加下划线(您可能需要这样做以表明这些全局变量是“非公开模块”。
PEP8提供了Python代码的编码约定,该Python代码包含主Python发行版中的标准库。遵循的次数越多,您就越接近原点。