在阅读fastapi源码时,这一行让我很模糊:
from starlette.testclient import TestClient as TestClient
为什么不只是:
from starlette.testclient import TestClient
?
从可执行代码的角度来看,两个不同的代码示例(使用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
不同)。