如何使 `from . import utils` 生效
我有以下的文件夹结构:
some-tools-dir/
base_utils.py
other_utils.py
some-tool.py
some-other-tool.py
some-other-tools-dir/
basetools -> symlink to ../some-tools-dir
yet-another-tool.py
在 other_utils.py
文件里,我有:
import base_utils
现在,在 yet-another-tool.py
文件里,我想做:
import basetools.other_utils
但是这样不行,因为 Python 不把 basetools
识别为一个 Python 包。所以我添加了一个空的 basetools/__init__.py
文件。现在,在 other_utils
里,我遇到了一个异常:
import base_utils
ImportError: No module named base_utils
于是我把那一行改成了:
from . import base_utils
现在 yet-another-tool.py
可以正常工作了。
不过,some-tool.py
就不再工作了。它导入了 other_utils
,在这里我又遇到了异常:
from . import base_utils
ValueError: Attempted relative import in non-package
现在,我可以在 some-tools-dir/*-tool.py
文件里添加这个变通方法:
import os, sys
__package__ = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
sys.path += [os.path.dirname(os.path.dirname(os.path.abspath(__file__)))]
__import__(__package__)
而且,还要把那些文件里的所有本地导入改成相对导入。
这样应该能解决问题。不过,这样看起来有点丑,而且我还得修改 sys.path
。我尝试了几种这个变通方法的不同写法,但我想支持多个 Python 版本,如果可能的话,这样就变得复杂了,特别是因为我有 Python 3.2,我不喜欢用 imp
模块,因为它已经被弃用了。而且,这样似乎只会变得更加复杂。
我是不是漏掉了什么?这一切看起来都很丑,而且对于一个似乎并不罕见的用例来说太复杂了(对我来说)。有没有更干净、更简单的变通方法?
我愿意做的一个限制是只支持 Python >=3.2,如果这样能简化什么的话。
2 个回答
你能把顶层根路径加到 PYTHONPATH 吗?
如果可以的话,你就可以把
__init__.py
这个文件放到某个工具目录(或者其他工具目录)里。
然后在 other_utils.py 里,你可以这样做:
from some-tools-dir import base_utils
而在 yet-another-tool.py 里,你可以这样做:
from some-tools-dir import other_utils
这样你就可以删除符号链接,拥有更好的命名空间管理。
(注意,这个回答是通过整合来自这个回答和这个问题的信息而成的,如果你觉得不错,记得给他们点赞哦)
这个方法看起来不那么复杂,而且至少在Python 2.7及以上版本中可以用:
if __name__ == "__main__" and __package__ is None:
import sys, os.path as path
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
from some_tools_dir import other_utils
我觉得你觉得这个问题难的主要原因是,因为在Python包里面有可执行脚本其实是不太常见的。Guido van Rossum(Python的创始人)甚至称它为“反模式”。通常情况下,你的可执行文件会放在包的根目录之上,然后可以直接使用:
from some_tools_dir import other_utils
这样就简单多了。
或者,如果你想执行一个在包里面的脚本,你实际上是把它作为包的一部分来调用(同样是从包的上级目录来执行):
python -m some_tools_dir.other_utils