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