我创建了一个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
环境变量中。然而,对于一些本来不应该成为问题的事情来说,这两者似乎都是次优的解决方法。我该怎么办?
你误会了。由于某种原因,你有两个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
,因为它应该是。
虽然我不完全同意你的包装结构,但你可以使用__all__
,也可能是迄今为止我见过的明星进口合法用途。 __init__.py
可以提供更多用途,而不仅仅是将您的文件夹标识为包或子包。
使用Star Import
在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
公开的API分离给临时用户。在这种情况下,请使用第二个选项。这里唯一的缺点是你必须更加小心保持package.__all__
和相应的导入正确同步。
注意
我见过的很多项目(尤其是numpy),使用这种技术将各个模块的属性导出到顶层。例如,如果你有一个函数package.package.Datasets.get_data
,它将被列在package.package.Datasets.__all__
中,它将导入pacakge.package.__init__
,附加到package.package.__all__
,然后被顶级包和package.__all__
引用。