Python包结构

16 投票
2 回答
8713 浏览
提问于 2025-04-16 16:11

我有一个Python包,里面有几个子包。

myproject/
  __init__.py
  models/
    __init__.py
    ...
  controllers/
    __init__.py
    ..
  scripts/
    __init__.py
    myscript.py

在myproject.scripts.myscript这个地方,我怎么才能访问到myproject.models呢?我试过

from myproject import models # No module named myproject
import models # No module named models
from .. import models # Attempted relative import in non-package

我之前也遇到过这个问题,但我总是记不住该怎么做。对我来说,这实在是太不直观了。

2 个回答

2

你的项目不在你的路径中。

选项 A

  • 安装你的包,这样 Python 就可以在任何地方通过它的绝对名称找到它(使用 from myproject import models

选项 B

  • 使用一些小技巧,把上级目录添加到你的路径中
  • sys.path.append(os.path.abspath('..'))

推荐使用第一个选项。

20

这是正确的版本:

from myproject import models

如果出现 ImportError: No module named foo 的错误,那是因为你没有设置 PYTHONPATH,导致它没有包含包含 myproject/ 的目录。

我担心其他人会建议一些技巧,让你避免设置 PYTHONPATH。我建议你不要听他们的。这就是 PYTHONPATH 存在的原因:告诉 Python 到哪里去找要加载的代码。它是可靠的,文档也写得比较清楚,而且可以在很多环境中使用。那些人为了避免设置它而使用的技巧,都不具备这些优点。

即使没有设置 PYTHONPATH,明确的相对导入是可以工作的,因为它可以沿着目录结构向上查找,直到找到正确的位置,而不需要先找到顶层再向下查找。不过,如果你把脚本作为命令行参数传给 python(或者直接用 #!/usr/bin/python 这一行调用),这个方法就不行了。这是因为在这两种情况下,它会成为进程的 __main__ 模块。对于 __main__ 来说,没有地方可以向上走——它已经在顶层了!如果你通过 导入 这个模块来调用你脚本中的代码,那就没问题。也就是说,比较一下:

python myproject/scripts/myscript.py

python -c 'import myproject.scripts.myscript'

你可以利用这一点,不直接执行你的脚本模块,而是创建一个 bin/myscript,在里面进行导入,并可能调用一个主函数:

import myprojects.scripts.myscript
myprojects.scripts.myscript.main()

可以参考 Twisted 的命令行脚本是如何定义的: http://twistedmatrix.com/trac/browser/trunk/bin/twistd

撰写回答