应用程序特定的PYTHONPATH
我有一个应用程序,它的结构是由多个包组成的层级关系。在这些包中,有很多模块需要引用更高层级的其他模块。就像下面的例子所示,我可以使用相对导入来解决这个问题。但是,直接运行模块进行测试时,会出现一个错误,提示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], '..')))
不过,这样的缺点是我需要在很多地方添加这段代码,而且它并不是固定的:'..'
的相对路径会根据包在层级中的深度而变化。
其他可能的解决方案...
- 我可以创建一个.pth文件,指向我的应用程序根目录。不幸的是,这个文件必须放在我的Python安装的site-packages文件夹中,所以它并不是很特定于我的应用程序。
- 我可以临时将我的应用程序根目录添加到PYTHONPATH,让所有模块都从根目录引用包。不过,这样就失去了通过简单调用
python.exe whatever.py
来运行脚本的便利性。 - 我可以写一个模块,当它被导入时,会在我的应用程序根目录中查找一个标记文件。这个通用模块可以安装到我的本地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基本上是为我做了第二种选择。