项目结构导致冗余点表示法

问题描述 投票:3回答:2

我创建了一个Python包,它基于Kenneth Reitz的“Repository Structure and Python”(1)中指出的结构。主要的包路径是:

/projects-folder (not site-packages)
    /package
        /package
            __init__.py
            Datasets.py
            Draw.py
            Gmaps.py
            ShapeSVG.py
            project.py
        __init__.py
        setup.py

使用当前结构,我必须使用以下模块导入语法:

import package.package.Datasets

我更愿意输入以下内容:

import package.Datasets

当然,我能够输入两次相同的单词,但从更深层次的意义上说它感觉不对,即我正在错误地构造我的包或者误解Python如何解释该结构。

根据文档(__init__.py),Python根本需要外部2来检测这个包。但是这会将/package/设置为包的顶层,并将/package/package/设置为子包,迫使我进入上面的笨重的导入语法。

为了避免这种情况,似乎我的选择是:

  • 创建一个包,其中外部文件夹包含顶级包模块。
  • 将内部文件夹添加到我的PYTHONPATH环境变量中。

然而,对于一些本来不应该成为问题的事情来说,这两者似乎都是次优的解决方法。我该怎么办?

python python-2.7 project-structure
2个回答
3
投票

你误会了。由于某种原因,你有两个package包,但你引用的来源从未说过这样做。使用setup.py的外部文件夹不应该是一个包。

听起来你在projects-folder中运行Python并尝试从那里导入你的包。这不是你应该做的。您有多种方法可以将包装到导入系统中。 (我将把setup.py中的文件夹称为setupfolder,以区别于内部文件夹):

  • 使用setup.py构建包,例如python setup.py bdist-wheel --universal,并使用pip安装内置包。
  • 跳过构建步骤,然后运行pip install path/to/setupfolder。如果要分发程序包,构建程序包会产生一个非常有用的安装程序,但您可能不希望这样做。
  • 使用pip install -e path/to/setupfolder在开发模式下“安装”软件包的源代码树,因此Python导入系统将在执行导入时找到软件包的源代码树。这很方便,因为如果编辑源存储库,则不必重新构建并重新安装,尽管您仍然希望重新启动正在使用该程序包的任何正在运行的Python进程。
  • 直接在setupfolder中运行Python。

任何这些选项都会导致您的包可以直接导入为package而不是package.package,因为它应该是。


1
投票

虽然我不完全同意你的包装结构,但你可以使用__all__,也可能是迄今为止我见过的明星进口合法用途。 __init__.py可以提供更多用途,而不仅仅是将您的文件夹标识为包或子包。

使用Star Import

package/package/__init__.py中,添加一个变量__all__,声明要导出的所有公共元素:

__all__ = ['Datasets', 'Draw', 'Gmaps', 'ShapeSVG', 'project']

package/__init__.pyfrom package.package import *。现在所有可用作package.package.x的属性也将作为package.x提供。

如果你想直接将package.package.__all__复制到package.__all__(这是可选的,但允许你正确地做from package import *),你可以做类似的事情

from package.package import *
from package.package import __all__ as _all
__all__ = _all
del _all

不使用星级导入

你可以在不使用package.package.__all__的情况下完成同样的事情。只需将__all__直接添加到package/__init__.py并使用from package.package import x样式的导入:

from package.package import (
    Datasets, Draw, Gmaps, ShapeSVG, project
)
# As before, package.__all__ is optional
__all__ = ['Datasets', 'Gmaps', 'ShapeSVG', 'project']

我仍然建议使用package.package.__all__变量,但对于此特定目的,它是可选的。

优点和缺点

这两种方法都非常合法,我已经看到两种方法都用于重大项目。第一种方法减少了冗余。您只在一个地方定义公共出口:package.package.__all__。明星进口和package.__all__直接引用这个定义,导致你真正需要维护的一个地方。另一方面,有时您想要将“完整”package.package.x API与您通过package.x公开的API分离给临时用户。在这种情况下,请使用第二个选项。这里唯一的缺点是你必须更加小心保持package.__all__和相应的导入正确同步。

注意

我见过的很多项目(尤其是numpy),使用这种技术将各个模块的属性导出到顶层。例如,如果你有一个函数package.package.Datasets.get_data,它将被列在package.package.Datasets.__all__中,它将导入pacakge.package.__init__,附加到package.package.__all__,然后被顶级包和package.__all__引用。

© www.soinside.com 2019 - 2024. All rights reserved.