如何编写好的/正确的包 __init__.py 文件

Posted

技术标签:

【中文标题】如何编写好的/正确的包 __init__.py 文件【英文标题】:How do I write good/correct package __init__.py files 【发布时间】:2010-12-29 00:15:10 【问题描述】:

我的包有以下结构:

mobilescouter/
    __init__.py #1
    mapper/
        __init__.py  #2
        lxml/
            __init__.py #3
            vehiclemapper.py
            vehiclefeaturemapper.py
            vehiclefeaturesetmapper.py
        ...
        basemapper.py
   vehicle/
        __init__.py #4
        vehicle.py
        vehiclefeature.py
        vehiclefeaturemapper.py
   ...

我不确定应该如何正确写入 __init__.py 文件。__init__.py #1 看起来像:

__all__ = ['mapper', 'vehicle']
import mapper
import vehicle

但是,例如 __init__.py #2 应该是什么样子?我的是:

__all__ = ['basemapper', 'lxml']
from basemaper import *
import lxml

什么时候应该使用__all__

【问题讨论】:

请注意,在代码中使用 import * 通常是非常糟糕的做法,应尽可能避免。这方面好的用例很少,但确实很少见。 PSA:如果您有兴趣学习如何编写好的命名空间包(新类型的包),请查看此示例包:github.com/pypa/sample-namespace-packages 【参考方案1】:

我自己的__init__.py 文件经常是空的。特别是,我从来没有将from blah import * 作为__init__.py 的一部分——如果“导入包”意味着将所有类型的类、函数等直接定义为包的一部分,那么我会在词法上复制@ 的内容987654324@ 改为包的__init__.py 并删除blah.py (源文件的乘法在这里不好)。

如果您坚持支持 import * 成语 (eek),那么使用 __all__(尽可能地列出您自己的姓名列表)可能有助于控制损害。一般来说,命名空间和显式导入是的东西,我强烈建议重新考虑任何基于系统地绕过一个或两个概念的方法!-)

【讨论】:

我个人更喜欢把东西分开,然后导入 *.原因是,尽管有折叠之类的东西,我仍然讨厌浏览包含太多类的文件,即使是相关的。 @stefano 考虑一个大框架。如果它使用import *,您必须无条件地接受所有框架,即使是您永远不会使用的功能。保持__init__.py 为空会给你更多的机会,而不仅仅是全有或全无的语义。想想扭曲。 如果保持为空,即使导入mobilescouter后,仍然无法使用mobilescouter.mapper或mobilescouter.vehicle或mobilescouter.whatever。是不是 import mobilescouter.A, mobilescouter.B..... 太冗长了? @sunqiang 这是个人的,但我不这么认为。 from mobilescouter import A, B 只是一行代码,你没有一个包含 666 个类并且每个类都有自己的文件的项目,对吧?如果您的代码中有两个或更多import *,则您正在用潜在的垃圾填充命名空间,很快您就会忘记A 的来源。如果上层包装也这样做?您正在抓取所有子包和子子包。就像python的禅宗所说,显式胜于隐式。 @mg,如果 init.py 文件中有一行“import A, B”,那么我可以使用以下语法调用 A(或 B):移动侦察员.A;如果我们使用“from mobilescouter import A, B”,那么它就是 A.something。有时只是这一行,我不记得 A 是 mobilescouter 的子包,我认为这会导致命名空间污染(尽管它比 ""from mobilescouter import *" 要好得多。我仍然更喜欢 "import pkgname" 给用户统一的公共接口。所以 init.py 做 import sub_pkgname 的事情。【参考方案2】:

__all__ 非常好——它有助于指导导入语句而不自动导入模块 http://docs.python.org/tutorial/modules.html#importing-from-a-package

使用__all__import *是多余的,只需要__all__

我认为在__init__.py 中使用import * 来导入包的最有力的原因之一是能够在不破坏现有应用程序的情况下重构已经发展为多个脚本的脚本。但是如果你从一开始就设计一个包。我认为最好将__init__.py 文件留空。

例如:

foo.py - contains classes related to foo such as fooFactory, tallFoo, shortFoo

然后应用程序增长,现在它是一个完整的文件夹

foo/
    __init__.py
    foofactories.py
    tallFoos.py
    shortfoos.py
    mediumfoos.py
    santaslittlehelperfoo.py
    superawsomefoo.py
    anotherfoo.py

那么初始化脚本可以说

__all__ = ['foofactories', 'tallFoos', 'shortfoos', 'medumfoos',
           'santaslittlehelperfoo', 'superawsomefoo', 'anotherfoo']
# deprecated to keep older scripts who import this from breaking
from foo.foofactories import fooFactory
from foo.tallfoos import tallFoo
from foo.shortfoos import shortFoo

以便为执行以下操作而编写的脚本在更改期间不会中断:

from foo import fooFactory, tallFoo, shortFoo

【讨论】:

我对“all”和逐行导入感到非常困惑。你的例子很有启发性。 我被“__all__import *是多余的”弄糊涂了,__all__是模块的消费者使用的,from foo import *是模块本身用来使用别人的.... using __all__ and import * is redundant, only __all__ is needed 那些是多余的?他们做不同的事情。 __version__.py 导入变量怎么样【参考方案3】:

您的__init__.py 应该有一个文档字符串

虽然所有功能都在模块和子包中实现,但您的包文档字符串是记录从何处开始的地方。例如,考虑python email package。包文档是介绍目的、背景以及包中的各种组件如何协同工作的介绍。如果您使用 sphinx 或其他包自动从 docstrings 生成文档,那么包 docstring 正是描述此类介绍的正确位置。

对于任何其他内容,请参阅 firecrow 和 Alex Martelli 的出色回答。

【讨论】:

__init__.py 的实际 email 包是否遵循此指南?我看到一个单行文档字符串并不能解释“包中的各种组件如何协同工作”。 @Gertlex 也许只在网络文档中。

以上是关于如何编写好的/正确的包 __init__.py 文件的主要内容,如果未能解决你的问题,请参考以下文章

从__init__.py中的函数导入模块将模块对象绑定到全局命名空间?

python中的包

__all__ 在 __init__.py 中不生效? [复制]

python中的包

__init__.py 导入还公开了我使用的模块,不仅是我自己的类[重复]

Python 包的制作(__init__.py)