使方法在包中的更高级别可用

Pas*_*ten 2 python package

考虑以下包结构:

foo/                          # package name
    spam/                     # module name
        __init__.py
        eggs.py               # contains "bar" method
        exceptions.py         # contains "BarException" class
Run Code Online (Sandbox Code Playgroud)

现在为了调用该bar方法,我们必须这样做

import spam
spam.eggs.bar()
Run Code Online (Sandbox Code Playgroud)

我愿意输eggs

现在我知道这是可能的import ... as(和from ... import),但是没有办法使方法在树的更高层可用吗?

不想诉诸的事情:

  • 很多from ... import ...
  • 把我的eggs.py代码放进__init__.py
  • 加星标的进口
  • 长名称,例如spam.exceptions.BarException(可能更长)

一个例子是exceptions.py我定义异常类的地方。

每当我想让它们可供用户使用时,我不希望他们使用spam.exceptions.BarException,而是能够使用spam.BarException

目标:

import spam
try:
    spam.bar()   # in this case throws BarException
except spam.BarException:
    pass
Run Code Online (Sandbox Code Playgroud)

aba*_*ert 5

请注意,与您的评论相反,顶部foo不是包名称,它只是(大概)位于您sys.path某处的目录,并且spam不是模块名称而是包名称,并且eggs是模块名称。所以:

foo/                          # directory package is in
    spam/                     # package name
        __init__.py
        eggs.py               # contains "bar" method
        exceptions.py         # contains "BarException" class
Run Code Online (Sandbox Code Playgroud)

你想做的事情的关键是:

  • 中的任何全局名称spam/__init__.py都是包的成员spam__init__.py它们实际上是在 中定义的,还是import从其他地方编辑的并不重要。

因此,如果您想让该spam.eggs.bar函数可用spam.bar,您只需将此行添加到spam/__init__.py

from .eggs import bar
Run Code Online (Sandbox Code Playgroud)

如果您有一个__all__属性来spam/__init__.py定义 的公共属性spam,您将需要添加bar到该列表中:

__all__ = ['other', 'stuff', 'directly', 'in', 'spam', 'bar']
Run Code Online (Sandbox Code Playgroud)

如果您想将所有公共内容重新导出spam.eggs为 的公共部分spam,您可以这样做:

from .eggs import *

__all__ = ['other', 'stuff', directly', 'in', spam'] + eggs.__all__
Run Code Online (Sandbox Code Playgroud)

当然,您可以将其扩展到多个子模块:

from .eggs import *
from .exceptions import *

__all__ = (['other', 'stuff', directly', 'in', spam'] + 
           eggs.__all__ +
           exceptions.__all__)
Run Code Online (Sandbox Code Playgroud)

这在 stdlib 和流行的第三方包中很常见。asyncio/__init__.py有关一个很好的示例,请参阅Python 3.4 的源代码。

然而,只有在这种情况下才真正常见您希望您的用户能够将您的包视为一个简单的扁平模块,但它实际上具有一些内部结构(因为否则实现会太复杂,或者因为有时用户会需要该结构)。如果你从孙子、兄弟姐妹或父母而不是孩子那里提取名字,你可能正在滥用这个习语(或者至少你应该停下来并说服自己你没有滥用)。