考虑以下包结构:
foo/ # package name
spam/ # module name
__init__.py
eggs.py # contains "bar" method
exceptions.py # contains "BarException" class
现在为了调用 bar
方法,我们必须做
import spam
spam.eggs.bar()
我想丢掉鸡蛋
。
现在我知道可以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
最佳答案
请注意,与您的评论相反,顶部 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
你想做的事情的关键是:
spam/__init__.py
中的任何全局名称都是spam
包的成员。它们实际上是在__init__.py
中定义的,还是从其他地方导入
的,这并不重要。
所以,如果你想让 spam.eggs.bar
函数作为 spam.bar
可用,你所要做的就是将这一行添加到 垃圾邮件/__init__.py
:
from .eggs import bar
如果你在 spam/__init__.py
中有一个 __all__
属性来定义 spam
的公共(public)属性,你会想要添加 bar
到那个列表:
__all__ = ['other', 'stuff', 'directly', 'in', 'spam', 'bar']
如果你想从 spam.eggs
中重新导出 everything public 作为 spam
的公共(public)部分,你可以这样做:
from .eggs import *
__all__ = ['other', 'stuff', directly', 'in', spam'] + eggs.__all__
当然,您可以将其扩展到多个子模块:
from .eggs import *
from .exceptions import *
__all__ = (['other', 'stuff', directly', 'in', spam'] +
eggs.__all__ +
exceptions.__all__)
这在 stdlib 和流行的第三方包中很常见。有关一个很好的例子,请参阅 asyncio/__init__.py
的来源来自 Python 3.4。
但是,它只在这种确切情况下才真正常见:您希望您的用户能够将您的包视为一个简单的平面模块,但它实际上有一些内部结构(要么是因为否则实现会太复杂,要么是因为用户偶尔会需要这种结构)。如果您使用的是孙子、 sibling 或 parent 而不是 child 的名字,则您可能在滥用习语(或者至少您应该停下来并说服自己没有滥用)。
关于python - 使方法在包中的更高级别可用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30429022/