Python中__all__的含义和作用
技术背景
在Python编程中,当我们使用from <module> import *
语句导入模块时,有时需要精确控制哪些符号(变量、函数、类等)会被导入。__all__
就是Python提供的一种机制,用于声明模块或包的“公共”接口,它可以让开发者明确指定在使用from <module> import *
时要导入的符号。
实现步骤
模块中使用__all__
在模块中,__all__
是一个字符串列表,定义了使用from <module> import *
时要导出的符号。例如,在foo.py
文件中:
1 2 3 4 5
| __all__ = ['bar', 'baz']
waz = 5 bar = 10 def baz(): return 'baz'
|
此时,使用from foo import *
只能导入bar
和baz
,而waz
不会被导入。如果没有定义__all__
,from <module> import *
会导入所有不以_
开头的符号。
包中使用__all__
在包的__init__.py
文件中,__all__
同样可以用来控制from <package> import *
的行为。例如,有如下包结构:
1 2 3 4
| package ├── __init__.py ├── module_1.py └── module_2.py
|
在__init__.py
中可以这样定义__all__
:
1 2 3
| from .module_1 import * from .module_2 import * __all__ = ['foo', 'Bar']
|
这样,当使用from package import *
时,只会导入foo
和Bar
。
核心代码
模块中__all__
的使用示例
1 2 3 4 5 6
| __all__ = ['bar', 'baz']
waz = 5 bar = 10 def baz(): return 'baz'
|
1 2 3 4 5 6 7
| from foo import *
print(bar) print(baz())
|
包中__all__
的使用示例
1 2 3 4
| from .module_1 import * from .module_2 import * __all__ = ['foo', 'Bar']
|
1 2 3 4
| __all__ = ['foo'] imp_detail1 = imp_detail2 = imp_detail3 = None def foo(): pass
|
1 2 3 4
| __all__ = ['Bar'] imp_detail1 = imp_detail2 = imp_detail3 = None class Bar: pass
|
1 2 3 4
| import package package.foo() package.Bar()
|
使用装饰器简化__all__
的定义
1 2 3 4 5 6 7 8 9
| 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
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from lib import export __all__ = []
@export def foo(): pass
@export def bar(): 'bar'
def main(): print('main')
if __name__ == '__main__': main()
|
最佳实践
- 在开发模块或包时,尽早定义
__all__
,让其他开发者清楚知道哪些是公共接口,哪些是实现细节。 - 如果模块或包的API经常变动,且还没有用户,可以暂时使用
_
前缀来标记私有符号,而不使用__all__
。 - 当使用
__all__
时,可以考虑使用装饰器来避免重复书写导出的符号名。
常见问题
1. __all__
对其他导入方式有影响吗?
__all__
只影响from <module> import *
和from <package> import *
的导入行为,对于其他导入方式,如import <module>
、from <module> import <name>
等没有影响。
2. 如果不定义__all__
会怎样?
- 在模块中,
from <module> import *
会导入所有不以_
开头的符号。 - 在包中,
from <package> import *
只会导入__init__.py
中定义的符号(除非有其他显式加载的子模块)。
3. __all__
必须是列表吗?
不一定,__all__
可以是任何字符串序列,如元组。使用元组可以节省一些内存和CPU周期,但如果只定义一个公共名称,不要忘记加逗号,如__all__ = ('some_name',)
。