Python中__all__的含义及使用
技术背景
在Python编程中,我们经常会使用import
语句来导入模块或包中的内容。当使用from <module> import *
这种通配符导入方式时,Python需要知道应该导入哪些内容。__all__
就是用来控制这种通配符导入行为的一个特殊变量。它可以在模块文件(包括__init__.py
文件)中使用,帮助我们明确指定哪些内容是模块或包的公共接口,哪些内容是内部实现细节。
实现步骤
1. 在模块中使用__all__
在一个普通的Python模块(.py
文件)中,__all__
是一个列表,其中包含了字符串形式的变量、函数或类的名称。当使用from <module> import *
时,只有__all__
中指定的内容会被导入。
1 2 3 4 5 6 7
| __all__ = ['bar', 'baz']
waz = 5 bar = 10 def baz(): return 'baz'
PYTHON
|
在另一个文件中使用通配符导入:
1 2 3 4 5 6
| from foo import *
print(bar) print(baz())
PYTHON
|
2. 在包中使用__all__
在Python中,一个包含__init__.py
文件的目录被视为一个包。__init__.py
文件可以用来初始化包,并且可以设置__all__
变量。当使用from <package> import *
时,__all__
中指定的模块或对象会被导入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from .module_1 import * from .module_2 import * __all__ = ['foo', 'Bar']
__all__ = ['foo'] imp_detail1 = imp_detail2 = imp_detail3 = None def foo(): pass
__all__ = ['Bar'] imp_detail1 = imp_detail2 = imp_detail3 = None class Bar: pass
PYTHON
|
在另一个文件中导入包:
1 2 3
| import package package.foo() package.Bar()
PYTHON
|
核心代码
模块中__all__的使用
1 2 3 4 5 6 7 8 9 10 11 12 13
| __all__ = ['public_function', 'PublicClass']
def public_function(): return "This is a public function."
class PublicClass: def __init__(self): self.message = "This is a public class."
def _private_function(): return "This is a private function."
PYTHON
|
1 2 3 4 5 6 7 8 9 10
| from module import *
print(public_function()) obj = PublicClass() print(obj.message)
PYTHON
|
包中__all__的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from .submodule1 import * from .submodule2 import * __all__ = ['submodule1_function', 'Submodule2Class']
__all__ = ['submodule1_function']
def submodule1_function(): return "This is a function from submodule1."
__all__ = ['Submodule2Class']
class Submodule2Class: def __init__(self): self.message = "This is a class from submodule2."
PYTHON
|
1 2 3 4 5 6
| from package import *
print(submodule1_function()) obj = Submodule2Class() print(obj.message)
PYTHON
|
最佳实践
明确公共接口
在开发模块或包时,尽早定义__all__
,让其他开发者清楚知道哪些内容是可以使用的公共接口,哪些是内部实现细节。
避免使用通配符导入
虽然__all__
可以控制通配符导入的行为,但通配符导入会使命名空间变得混乱,建议尽量使用明确的导入方式,如from <module> import <name>
。
使用导出装饰器
为了避免在__all__
中重复书写函数和类的名称,可以使用导出装饰器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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
from lib import export __all__ = []
@export def foo(): pass
@export def bar(): 'bar'
def main(): print('main')
if __name__ == '__main__': main()
PYTHON
|
常见问题
1. 不定义__all__时的行为
如果在模块中不定义__all__
,使用from <module> import *
会导入所有不以下划线开头的名称。如果在包的__init__.py
中不定义__all__
,from <package> import *
只会导入__init__.py
中定义的名称,而不会自动导入包中的子模块。
2. __all__是否影响其他导入方式
__all__
只影响from <module> import *
和from <package> import *
这种通配符导入方式。其他导入方式,如import <module>
、from <module> import <name>
不受__all__
的影响。
3. __all__必须是列表吗
根据Python文档,__all__
必须是一个由字符串组成的序列,所以也可以使用元组,例如__all__ = ('some_name',)
。