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

时间:2017-12-14 17:26:21

标签: python python-2.7 project-structure

我创建了一个Python包,它基于Kenneth Reitz指出的结构。 "存储库结构和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如何解释该结构。

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

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

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

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

2 个答案:

答案 0 :(得分: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 :(得分:1)

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

使用星空导入

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

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

package/__init__.py执行from 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向临时用户公开。在这种情况下,请使用第二个选项。这里唯一的缺点是你必须更加谨慎地保持package.__all__并且相应的导入正确同步。

注意

我见过的很多项目(尤其是numpy),使用这种技术将各个模块的属性导出到顶层。例如,如果您有一个函数package.package.Datasets.get_data,它将列在package.package.Datasets.__all__中,它将导入pacakge.package.__init__,附加到package.package.__all__,然后由package.__all__引用顶级包和long L1; if ((L%N1) == 0) { L1 = Convert.ToInt64(L1_temp - 1); }else { L1 = Convert.ToInt64(Math.Floor(L1_temp)); }