__import__()为何调用__init__.py两次?

4 投票
2 回答
1298 浏览
提问于 2025-04-16 04:20

我在想,为什么在加载一个包的时候,__import__() 会调用 __init__ 模块两次。

test.py
testpkg/
        __init__.py

test.py:

pkg = __import__("testpkg", fromlist=[''])

__init__.py:

print "Called."

当我运行 python test.py 时,Called. 会被打印两次。为什么 Python 会执行 __init__ 这个“模块”两次呢?

2 个回答

5

使用 fromlist=[''] 这种方法来导入特定模块是被 Python 开发者明确反对的。虽然这个问题已经被提出来了,但解决的可能性不大,因为大家认为这其实是对 fromlist 的误用,而不是一个真正的bug,而且有更好的解决办法。

你应该使用 importlib.import_module 来导入模块(这个在 Python 2.7 和 Python 3.1 的标准库中都有,或者你可以从 PyPI 下载,支持到 Python 2.3,并且在 Django 1.1 之后也包含了这个功能,叫做 django.utils.importlib)。这样可以避免这个问题的发生,提供了更好的模块导入接口,甚至在指定导入的包时,还能使用相对导入。

如果你真的不能使用 importlib(比如说不允许使用 PyPI 的依赖,尽管你可以自由复制代码,因为有 PSF 许可证,而且代码也很短),那么你应该使用 __import__("some.module"); mod = sys.modules["some.module"]。这是官方认可的解决方案(但只有在无法使用 importlib 的情况下才用)。

5

这是一个Python的bug。把空字符串作为fromlist的元素是不合法的,应该会引发一个错误。

其实在fromlist里不需要包含"";因为模块本身总是会被加载,这个是默认的。真正发生的事情是module.submodule这个字符串用了空字符串,导致模块名变成了testpkg.,后面多了一个点。这样的话,它会被直接导入,而因为它的名字和testpkg不一样,所以它被当作一个独立的模块导入了。

试试这个:

pkg = __import__("testpkg", fromlist=[''])
import sys
print sys["testpkg"]
print sys["testpkg."]

...你会看到重复的模块。

如果还没有人提这个问题,可能应该有人去反馈一下;我现在太累了,没力气去做。

撰写回答