为什么导入带点模块名时会因当前目录中的模块匹配高层级而失败?

2 投票
1 回答
727 浏览
提问于 2025-04-16 04:53

我在尝试使用Python的一个标准库模块,咱们就叫它 foo.bar.baz

于是我写了一个小脚本,开头是

import foo.bar.baz

然后把它保存为 foo.py

当我运行这个脚本时,出现了一个导入错误(ImportError)。我花了一段时间才搞明白(我还在学习Python),最终意识到问题出在我给脚本起的名字上。一旦我把 foo.py 改成其他名字,问题就解决了。

所以我明白了,import foo 这个语句会先找 foo.py 这个脚本,然后才会去找标准库里的 foo。但我不太明白,当我说 import foo.bar.baz 时,它到底在找什么?有没有可能 foo.py 里面的内容能让这个语句成立?如果没有,为什么Python解释器不继续去找像 foo/bar 这样的目录结构,并且里面有合适的 __init__.py 文件呢?

1 个回答

6

import foo.bar.baz 这样的导入语句,首先会导入 foo,然后再从 foo 中获取 bar,接着再从 foo.bar 中获取 baz。至于 foo 在被导入后,是否能满足对 barbar.baz 的请求,这对导入 foo 来说并不重要。它只是一个模块。实际上,只有一个 foo 模块。无论是 import foo 还是 import foo.bar.baz,都会找到同一个模块——就像用其他方式导入 foo 模块一样。

实际上,有一种方法可以让 foo 成为一个单独的模块,而不是一个包,同时还能满足像 import foo.bar.baz 这样的语句:它可以把 "foo.bar""foo.bar.baz" 添加到 sys.modules 字典中。这正是 os 模块对 os.path 所做的:它会根据平台导入正确的 "path" 模块(比如 posixpathntpathos2path 等),并把它赋值给 path 属性。然后,它会执行 sys.modules["os.path"] = path,这样就可以通过 os.path 导入这个模块,所以像 import os.path 这样的语句就能正常工作。其实这样做并没有什么必要——因为 os.path 在不导入的情况下也可以使用——但这是可能的。

撰写回答