为什么使用“from module import A as A”而不是“from module import A”

Wak*_*eng 9 python mypy

在阅读fastapi的源代码时,这一行让我很模糊:

from starlette.testclient import TestClient as TestClient
Run Code Online (Sandbox Code Playgroud)

为什么不只是:from starlette.testclient import TestClient

met*_*ter 14

从可执行代码的角度来看,两个不同的代码示例(使用Python 3.9)生成的Python字节码绝对没有区别:

>>> dis.dis('from starlette.testclient import TestClient as TestClient')
  1           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (('TestClient',))
              4 IMPORT_NAME              0 (starlette.testclient)
              6 IMPORT_FROM              1 (TestClient)
              8 STORE_NAME               1 (TestClient)
             10 POP_TOP
             12 LOAD_CONST               2 (None)
             14 RETURN_VALUE
>>> dis.dis('from starlette.testclient import TestClient')
  1           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (('TestClient',))
              4 IMPORT_NAME              0 (starlette.testclient)
              6 IMPORT_FROM              1 (TestClient)
              8 STORE_NAME               1 (TestClient)
             10 POP_TOP
             12 LOAD_CONST               2 (None)
             14 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

如图所示,它们完全相同。(相关线程线程。)

然而,Graham501617 的评论指出了现代类型提示验证器(例如mypy)如何接受这种特定语法来表示该导入名称的重新导出(另一个是__all__,值得庆幸的是,它们最终确实正确支持,因为这是一个标准自 Python 2 以来表示要(重新)导出的符号的语法。具体来说,根据引用的 PEP 0484 中存根文件的描述,引用:

  • 导入到存根中的模块和变量不被视为从存根导出,除非导入使用该import ... as ...形式或等效from ... import ... as ...形式。(更新:澄清一下,这里的目的是仅导出使用表单导入的名称X as X,即前后的名称as必须相同。)

TestClient这意味着该库可能遵循该特定约定,以方便从问题中引用的存根(模块)文件中重新导出名称。事实上,git blame在包中查找相关文件指向此提交直接链接到文件的相关差异),该提交引用了此问题,其中包含类似的简短讨论以解决确切的类型提示问题;这样做是为了确保 mypy 将这些导入的名称视为重新导出,从而允许使用该--no-implicit-reexport标志(--strict可能已隐式启用)。