如何使 `from . import utils` 生效

0 投票
2 回答
12195 浏览
提问于 2025-04-18 13:50

我有以下的文件夹结构:

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 个回答

0

你能把顶层根路径加到 PYTHONPATH 吗?

如果可以的话,你就可以把

__init__.py

这个文件放到某个工具目录(或者其他工具目录)里。

然后在 other_utils.py 里,你可以这样做:

from some-tools-dir import base_utils

而在 yet-another-tool.py 里,你可以这样做:

from some-tools-dir import other_utils

这样你就可以删除符号链接,拥有更好的命名空间管理。

1

(注意,这个回答是通过整合来自这个回答这个问题的信息而成的,如果你觉得不错,记得给他们点赞哦)

这个方法看起来不那么复杂,而且至少在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

撰写回答