在包中提供更高级别的方法

时间:2015-05-24 22:34:21

标签: python package

考虑以下包结构:

foo/                          # package name
    foo/                      # module name
        __init__.py           
        foo.py                # contains "bar" method
        exceptions.py         # contains "BarException" class

现在为了调用bar方法,我们必须这样做(我想丢失一个foo):

import foo
foo.foo.bar() 

现在我知道有可能"导入... as" (和from ... import),但是没有办法让方法在树中更高可用吗?

想要诉诸的事情:

  • 很多from ... import ...
  • 将我的foo.py代码改为__init__.py
  • 已加星标的进口
  • 长{@ 1}}(可能更长)

一个例子是让foo.exceptions.BarException定义我的异常类。

每当我想让用户可以使用它们时,我都不希望它们exceptions.py,而是能够呼叫foo.exceptions.BarException

目标:

foo.BarException

2 个答案:

答案 0 :(得分:2)

__init__.py中,您可以从包中的其他模块导入内容。如果您__init__.py from .foo import bar import foo,那么有人可以foo.bar访问__init__.py。如果在from .exceptions import BlahException import foo foo.BlahException,则有人可以Blah然后执行from

但是,你应该警惕这一点太过分了。在包和模块中使用嵌套有一个目的,即创建单独的命名空间。明确地将一些常见的东西从子模块导入到顶层是很好的,但是如果你开始尝试隐式地使所有内容在顶层可用,那么你就会为自己设置名称冲突(例如,如果一个模块定义了一个名为__init__.py然后另一个模块也会这样做,但没有意识到当它们都导入到顶级时它们会发生碰撞。

“强制”用户使用foo并不是一个繁重的要求。如果使用你的库需要繁琐的导入,这可能表明你的包/模块结构过于繁琐,你应该结合一些东西,而不是将它们分成单独的目录/文件。

顺便提一下,您在帖子中指出的文件结构存在一些问题。您显示的顶级foo不是包,因为它没有foo。第二级foo不是模块,因为它不是文件。在您的示例中,第二级foo.py是一个包,其中包含一个名为foo的模块(在文件Undefined symbols for architecture armv7:"_SecCertificateCopyData", referenced from:-[AFSecurityPolicy evaluateServerTrust:forDomain:] in AFSecurityPolicy.o中);顶级[trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];目录在Python打包系统中没有状态。

答案 1 :(得分:2)

如果您的软件包和子模块名称不同,这将更容易理解,因此我会假装您这样做,并让您根据需要进行翻译。另请注意,与您的评论相反,前foo不是软件包名称,它只是某个目录(可能是)在sys.path某处,而且第二个foo不是模块名称而是包名称,第三个foo是模块名称。所以:

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__.pyimport来自其他地方定义并不重要。

因此,如果您想将spam.eggs.bar功能设为spam.bar,您只需将此行添加到spam/__init__.py

from .eggs import bar

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

__all__ = ['other', 'stuff', 'directly', 'in', 'spam', 'bar']

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

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和流行的第三方软件包中很常见。有关一个很好的示例,请参阅Python 3.4中的asyncio/__init__.py源代码。

然而,它只在这个完全的情况下才真正常见:你希望你的用途能够像对待一个简单的扁平模块那样对待你的包,但它实际上有一些内部结构(或者因为实现过于复杂,或者因为偶尔用户需要这种结构)。如果你从孙子,兄弟姐妹或父母而不是孩子那里取名,你可能会滥用这个成语(至少你应该停下来并说服自己,你不是)。