在 Python 中导入模块是否会在本地范围内执行模块的所有导入?

Does importing a module in Python execute all the module's imports in local scope?

$ python -c 'import urllib.parse; print(urllib.error.HTTPError)'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AttributeError: module 'urllib' has no attribute 'error'

这是意料之中的。我还没有导入 urllib.error 模块。

$ python -c 'import urllib.request; print(urllib.request.HTTPError)'
<class 'urllib.error.HTTPError'>

这个也是。 urllib.request imports urllib.error:

from urllib.error import URLError, HTTPError, ContentTooShortError

因此可以通过 urllib.request 获得名称。

$ python -c 'import urllib.request; print(urllib.error.HTTPError)'
<class 'urllib.error.HTTPError'>

但不是这个。是什么让我能够通过 urllib.error 访问 HTTPError?我读过 the docs,但没有看到任何线索。

不确定这是否符合您的预期,但这是设计。你看看 urllib/parse.py 和 urllib/request.py 的来源你会发现一个重要的区别:前者只导入 re、sys 和 collections 模块,而后者(除了其他不相关的模块) ) 还明确地从 urllib.error、urllib.parse 和 urllib.response.

导入符号

这就是为什么即使导入不在您自己的源文件中,您也会发现它们可用的原因。

更详细的答案需要对 Python 进口机械进行详细描述,这远远超出了 SO 答案中的适当范围。更深入的解释请参考官方文档

好的,让我们看看:

import urllib.parse
globals()

您将看到一个项目:

'urllib': <module 'urllib' from 'C:\Users\XXXX\AppData\Local\Programs\Python\Python35\lib\urllib\__init__.py'>

然后,尝试dir(urllib),你会看到:

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'parse']

所以urllib中有一个parse,但没有requesterror。因为 parse 不导入它们。

现在让我们去urllib.request:

import urllib.request
globals()

获得一件物品:

'urllib': <module 'urllib' from 'C:\Users\XXXX\AppData\Local\Programs\Python\Python35\lib\urllib\__init__.py'>

import urllib.parse相同。

然后再试一次dir(urllib)

['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'error', 'parse', 'request', 'response']

里面有'error', 'parse', 'request', 'response'个。为什么? 因为 urllib.request 导入它们

让我试着更详细地解释一下。导入 urllib.request 首先导入 urllib,然后是 urllib.request。导入 urllib 会在局部范围内为我们提供 urllib 符号,表示已导入模块。导入 urllib.request 会导入 urllib.error 等等。它将 error 符号添加到 urllib 模块。并且 因为我们引用了 urllib,所以我们可以在导入 urllib.request 之后访问 urllib.error。考虑以下示例:

./main.py

#!/usr/bin/env python
import a.b
print(a.c.c)   # 2
print(d.e.e)   # NameError

./a/b.py

import a.c
import d.e
b = 1

./a/c.py

c = 2

./d/e.py:

e = 3