应用程序特定的PYTHONPATH

5 投票
2 回答
1431 浏览
提问于 2025-04-16 22:02

我有一个应用程序,它的结构是由多个包组成的层级关系。在这些包中,有很多模块需要引用更高层级的其他模块。就像下面的例子所示,我可以使用相对导入来解决这个问题。但是,直接运行模块进行测试时,会出现一个错误,提示Attempted relative import in non-package

有没有更好的方法来组织我的应用程序或者我的导入语句,这样模块既可以单独执行进行测试,又可以从其他模块中导入呢?

例子

\spam
    \morespam
        child.py
    base.py
\eggs
    user.py

base.py

class Base(object):
    def hello(self):
        print 'Um, wot?'

child.py

from ..base import Base       # references the parent package correctly, 
                              # but fails when this module is executed individually

class Child(Base):
    def hello(self):
        print 'Hello, world!'

if __name__ == '__main__':
    import unittest
    # ... unit test code ...

user.py

from spam.morespam.child import Child
print Child().hello()

现有解决方案

我发现可以在需要引用更高层模块的模块顶部添加以下代码:

if __name__ == '__main__':
    import sys, os
    sys.path.append(os.path.abspath(os.path.join(sys.path[0], '..')))

不过,这样的缺点是我需要在很多地方添加这段代码,而且它并不是固定的:'..'的相对路径会根据包在层级中的深度而变化。

其他可能的解决方案...

  1. 我可以创建一个.pth文件,指向我的应用程序根目录。不幸的是,这个文件必须放在我的Python安装的site-packages文件夹中,所以它并不是很特定于我的应用程序。
  2. 我可以临时将我的应用程序根目录添加到PYTHONPATH,让所有模块都从根目录引用包。不过,这样就失去了通过简单调用python.exe whatever.py来运行脚本的便利性。
  3. 我可以写一个模块,当它被导入时,会在我的应用程序根目录中查找一个标记文件。这个通用模块可以安装到我的本地Python环境中,并被我应用中的每个模块导入,从而确保无论我执行哪个模块,根目录都在PYTHONPATH中。

2 个回答

1

你需要在想要进行导入的文件夹里添加一个空的 __init__.py 文件。这样做会让 Python 把这些文件夹当作包来处理,这样你就可以在导入语句中使用它们了:

\spam
    \morespam
        __init__.py
        child.py
    __init__.py
    base.py
\eggs
    __init__.py
    user.py

一旦你这样做了,并且把你的 PYTHONPATH 设置为这个目录的根目录,你就可以进行导入了:

base.py:

from morespam.child import Child
from ..eggs import user

虽然 __init__.py 文件只需要存在,但你也可以在这个文件里定义一个变量 __ALL__,它是一个包含你想要导入的模块列表。如果有脚本尝试使用 import *,就会导入这个列表里的模块。

1

我用PyCharm解决了这个问题。每次运行我的应用程序时,它会自动把我的源代码根目录添加到PYTHONPATH里。根据我的理解,PyCharm基本上是为我做了第二种选择。

撰写回答