我创建了一个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
环境变量。然而,对于那些首先不应该成为问题的事情来说,这些似乎都是次优的解决方法。我该怎么办?
答案 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
。这些选项中的任何一个都会导致您的包可以直接导入为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));
}
。