【问题标题】:Why do packages have imports under __init__.py file?为什么包在 __init__.py 文件下有导入?
【发布时间】:2018-01-13 14:55:00
【问题描述】:

例如在multiprocessing 包中,我们可以使用from multiprocessing import Process 导入类Process。为什么不from multiprocessing.context import Process 真正属于它?

其实我发现它们是一样的。为什么?

【问题讨论】:

  • 我们能不能把标题改成为什么有些包在__init__.py文件下有导入?这对搜索引擎更友好。

标签: python import packages python-import


【解决方案1】:

__init__ 添加导入通常是为了缩短导入路径并定义公共接口。 multiprocessing.context import Process 是一个内部接口,将来可以更改而不保持任何向后兼容性。

另一方面,multiprocessing import Process 是记录在案的公共接口,不会更改以破坏向后兼容性。

您可以看到__all__ under context.py 是空的,这意味着它没有公共接口,您不应该在您的应用程序中从它导入,因为它将来可能会在没有任何警告的情况下更改。

__all__ = []            # things are copied from here to __init__.py

来自 PEP-008 的相关部分:

Public and internal interfaces

任何向后兼容性保证仅适用于公共接口。因此,重要的是用户能够清楚地 区分公共接口和内部接口。

文档化的接口被认为是公开的,除非文档明确声明它们是临时的或内部的 接口免除通常的向后兼容性保证。 所有未记录的接口都应假定为内部接口。

为了更好地支持自省,模块应在其公共 API 中使用 __all__ 属性显式声明名称。环境 __all__ 为空列表表示该模块没有公共 API。

即使__all__ 设置得当,内部接口(包、模块、类、函数、属性或其他名称) 仍应以单个前导下划线作为前缀。

如果任何包含命名空间(包、模块或类)被认为是内部的,那么接口也被认为是内部的。

应始终将导入的名称视为实现细节。其他模块不得依赖于间接访问此类 导入的名称,除非它们是明确记录的部分 包含模块的 API,例如 os.path 或包的 __init__ 从子模块公开功能的模块。


在我看来,著名的requests 库有一个非常好的公共接口,你可以看到它是通过在__init__.py 文件中导入大部分内容来完成的。而且您会发现它也是基于 __init__.py 文件下的导入记录的。

【讨论】:

    【解决方案2】:

    from multiprocessing import Process 有效,因为 Process 被导入到 multiprocessing 包的 __init__.py 中。例如,在您的 shell 中,键入以下代码:

    import multiprocessing
    with open(multiprocessing.__file__, 'r') as f:
        print(f.readlines())
    

    你会看到这些线条:

    from . import context
    
    #
    # Copy stuff from default context
    #
    
    globals().update((name, getattr(context._default_context, name))
                     for name in context._default_context.__all__)
    __all__ = context._default_context.__all__
    

    所以是的,是一样的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-26
      • 1970-01-01
      • 1970-01-01
      • 2017-07-25
      • 2014-08-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多