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

问题描述 投票:0回答:1

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

from starlette.testclient import TestClient as TestClient

为什么不只是:

from starlette.testclient import TestClient

python mypy
1个回答
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

如图所示,它们完全相同。 (相关问题PEP-0484存在之前询问)。

然而,Graham501617的评论指出了现代类型提示验证器(例如

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

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

查看

git blame
指向此提交的包中的相关文件(直接链接到文件的相关差异)表明此特定类型提示问题正在得到解决(作为此问题的一部分) )以确保 mypy 将这些导入的名称视为重新导出,从而允许使用
--no-implicit-reexport
标志(
--strict
可能已隐式启用)。

这种特殊的重新导出语法对于“

as
之前和之后的名称必须相同”非常严格,相关问题中提到的语法(即
import foo.bar as bar
)可以在某些现代软件包的推荐中找到(例如,PyTorch 建议使用
import torch.nn as nn
,如这个问题中所述),实际上不允许从当前模块重新导出
bar
(或
nn
)符号,因为
foo.bar
不是与
bar
相同(同样
torch.nn
nn
不同)。

© www.soinside.com 2019 - 2024. All rights reserved.