The __init__.py files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name, such as string, from unintentionally hiding valid modules that occur later on the module search path.
In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package or set the __all__ variable.
from .Bar_implementation import *
from .Baz_implementation import *
__all__ = ['Bar', 'Baz']
如果您还没有准备好在顶级API中发布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
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()
无论是作为主函数运行还是由另一个函数导入,这都可以正常工作。
$ 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
链接到,但这里没有明确提到,正是在使用
__all__
时。它是一个字符串列表,定义在模块上使用from <module> import *
时将导出模块中的哪些符号。例如,
foo.py
中的以下代码显式导出符号bar
和baz
:然后,可以像这样导入这些符号:
如果上面的
__all__
被注释掉,那么这段代码将执行到完成,因为import *
的默认行为是从给定的命名空间导入所有不以下划线开头的符号。引用:https://docs.python.org/tutorial/modules.html#importing-from-a-package
注意:
__all__
仅影响from <module> import *
行为。在__all__
中未提及的成员仍然可以从模块外部访问,并且可以使用from <module> import <member>
导入。__all__
做什么?它从模块中声明语义上的“public”名称。如果
__all__
中有一个名称,那么用户应该使用它,并且他们可以期望它不会改变。它还将产生程序性影响:
import *
__all__
在模块中,例如module.py
:意味着当您从模块中
import *
时,只导入__all__
中的那些名称:文档工具
文档和代码自动完成工具可能(实际上,应该)还检查
__all__
,以确定从模块中显示的可用名称。__init__.py
使目录成为Python包从docs:
因此
__init__.py
可以声明包的__all__
。管理API:
包通常由模块组成,这些模块可以相互导入,但它们必须与
__init__.py
文件绑定在一起。这个文件使目录成为一个实际的Python包。例如,假设包中包含以下文件:让我们用Python创建这些文件,这样您就可以继续-您可以将以下内容粘贴到Python 3 shell中:
现在您已经提供了一个完整的api,其他人可以在导入您的包时使用它,例如:
当创建模块时,包中不会有您使用的所有其他实现细节,这些模块会使
package
名称空间变得杂乱无章。__all__
在__init__.py
中经过更多的工作,也许你已经决定模块太大了(像成千上万行?)需要分开。因此,您可以执行以下操作:
首先使用与模块相同的名称创建子包目录:
移动实现:
为每个声明
__all__
的子包创建__init__.py
:现在仍然在包级别配置了api:
而且,您可以很容易地向API添加可以在子包级别而不是子包的模块级别管理的内容。如果要向API添加新名称,只需更新
__init__.py
,例如在模块2中:如果您还没有准备好在顶级API中发布
Baz
,那么在顶级API中__init__.py
您可以:如果您的用户知道
Baz
的可用性,他们可以使用它:但如果他们不知道,其他工具(比如pydoc)不会通知他们。
稍后,当
Baz
准备好进入黄金时段时,您可以更改:前缀
_
与__all__
:默认情况下,Python将导出所有不以} :
_
开头的名称。你当然可以依赖这个机制。事实上,Python标准库中的某些包确实依赖于此,但要这样做,它们会将导入别名为,例如在^{使用
_
约定可能更优雅,因为它消除了再次命名名称的冗余。但是它增加了导入的冗余(如果你有很多导入的话),忘记一致地做这件事是很容易的,而且你最不想做的就是无限期地支持一些你只想成为实现细节的东西,仅仅因为你在命名函数时忘了加上前缀_
。我个人在模块开发生命周期的早期为模块编写了一个
__all__
,这样其他可能使用我的代码的人就知道他们应该使用什么而不是使用什么。标准库中的大多数包也使用
__all__
。当避免
__all__
时是有意义的在以下情况下,可以使用
_
前缀约定代替__all__
:装饰器
使用
__all__
的缺点是,必须编写两次导出的函数和类的名称,并且信息与定义分开保存。我们可以用装饰器来解决这个问题。我是从大卫·比兹利关于包装的演讲中得到这样一个出口装潢师的主意的。这个实现在CPython的传统导入器中似乎工作得很好。如果您有一个特殊的导入钩子或系统,我不保证,但是如果您采用它,退出是相当简单的—您只需要手动将名称添加回
__all__
例如,在实用程序库中,您可以定义decorator:
然后,在定义
__all__
的地方,执行以下操作:无论是作为主函数运行还是由另一个函数导入,这都可以正常工作。
使用
import *
的API设置也可以工作:它是该模块的公共对象列表,由
import *
解释。它覆盖了隐藏以下划线开头的所有内容的默认设置。相关问题 更多 >
编程相关推荐