Python打包中的相对导入
首先,我很抱歉,我知道关于相对导入的问题已经有很多了,但我就是找不到解决办法。如果可以的话,我想使用以下的目录结构:
myClass/
__init__.py
test/
demo.py
benchmark.py
specs.py
src/
__init__.py
myClass.py
现在我有几个问题:
包内的测试文件应该怎么正确导入 myClass.py 呢?
如果你想从外部导入这个包,假设你把 myClass 当作 libs/myClass 或 include/myClass 的子模块,该怎么做呢?
到目前为止,我还没找到一个优雅的解决方案。根据我理解的 Guido 的决定,应该可以用 from ..src import myClass
这样的方式,但这会报错:
ValueError: Attempted relative import in non-package
这看起来是因为它没有把 myClass 当作包来处理。根据 文档 的说明:
__init__.py 文件是让 Python 把目录当作包的必要条件;
看起来我缺少了一些东西来指定包的脚本在哪里,我应该使用 .pth 文件吗?
3 个回答
包内引用讲的是如何从 test/*
中导入 myClass
。如果你想从外部导入这个包,你需要在运行导入程序之前,把它的路径添加到 PYTHONPATH
环境变量中,或者在代码中导入之前,把它加到 sys.path
列表里。
为什么 from ..src import myClass
会失败:可能是因为 src
不是一个Python包,所以你不能从那里导入。你需要像上面说的那样,把它添加到Python路径中。
昨晚我花了好几个小时终于找到了Python中相对导入的问题的答案!或者说,至少找到了一个简单的解决办法。解决这个问题的最好方法是从另一个模块来调用这些模块。比如说,你想让 demo.py
导入 myClass.py
。在 myClass
文件夹里,作为子包的根目录,它们需要有一个文件来调用这两个模块。根据我的理解,工作目录总是被认为是 __main__
,所以如果你从 demo.py
脚本测试导入,你会遇到错误。为了说明这一点:
文件夹结构:
myClass/
main.py #arbitrary name, can be anything
test/
__init__.py
demo.py
src/
__init__.py
myClass.py
myClass.py:
def randomMaths(x):
a = x * 2
y = x * a
return y
demo.py:
from ..src import myClass
def printer():
print(myClass.randomMaths(42))
main.py:
import test.demo
demo.printer()
如果你在解释器中运行 demo.py
,你会产生一个错误,但运行 main.py
就不会。这有点复杂,但确实有效 :D
ValueError: Attempted relative import in non-package
这个错误的意思是你在一个不是包的模块里尝试使用相对导入。问题出在包含这个 from ... import
语句的文件上,而不是你想要导入的文件。
所以,如果你在测试中使用相对导入,比如说,你应该让你的测试成为你包的一部分。这意味着:
- 在 test/ 文件夹里添加一个
__init__.py
文件。 - 通过一些外部脚本来运行它们,比如 nosetests。
如果你像这样运行 python myClass/test/demo.py
,相对导入也不会起作用,因为你是把 demo 模块当作普通文件在运行,而不是作为包。相对导入要求使用它的模块本身也要作为包模块被导入,比如 from myClass.test.demo import blabla
,或者使用相对导入。