导入语句是如何找到那个模块的?

How did the import statement find that module?

给定以下目录结构:

here/
├── app
│   ├── __init__.py
│   ├── json.py
│   └── example.py
└── my_script.py

__init__.pyjson.py 是空文件。

my_script.py 的内容:

from app import example

example.py

的内容
import importlib, imp, sys, os

# ensures '' is not in sys.path
sys.path = [p for p in sys.path if p]

# ensures PYTHONPATH, if any, is not over-reaching
os.environ.pop('PYTHONPATH', None)

# ensures we do not see json.py in the cwd
assert not os.path.isfile('json.py')

print '1: ', imp.find_module('json')
print '2: ', __import__('json')
print '3: ', importlib.import_module('json')

import json
json.loads

现在,从 here 目录,执行:

python ./my_script.py

你会看到方法1、2、3,都找到了json模块的核心库版本。

然而,实际的导入语句仍然设法以某种方式获取空的 json.py 文件 (AttributeError: 'module' object has no attribute 'loads')。

我的理解是json这里的包版本应该只能通过命名空间访问,即from app import json,但命名空间在这里似乎不起作用。

在 python3,我无法重现该问题。我还注意到,如果我们将 from __future__ import absolute_import 放入 example.py 文件中,问题就会消失。

import语句如何找到本地文件,为什么会隐藏核心库版本?

编辑: 另外一点,当我们到达行 import json 时,已经有一个 json 模块加载到 sys.modules 从上面的行。那么为什么 python 再次尝试导入模块,难道不应该简单地使用模块缓存中已有的模块吗?

到这里你已经差不多找到答案了。 Python 2.x 默认情况下会先进行包相关导入,其中包括 "shadowing" 基础包的潜力。

请参阅 python 2 文档中有关 Intra-package References 的部分。

在 Python 2.5 中实际上引入了指定显式相对导入的能力以及 from __future__ import absolute_import,这在 PEP 328 中有进一步解释。此行为成为 Python 中的默认行为 3. 新行为(假定绝对和显式相对导入)在很大程度上是明确实现的,以解决您提出的问题(隐藏内置模块),尽管它也允许更好地控制多级别相对导入语法(即 .. 用于父模块,... 用于更高级别,依此类推。)