正如 这个伟大的答案 所建议的那样,从Python 3.7开始,如果使用
from __future__ import annotations
指令,则可以在类型注释中使用前向声明。
但是,如果我想创建,这仍然不起作用注释类型的别名:
from __future__ import annotations
import typing
MyType1 = typing.Union[str, MyType2]
MyType2 = typing.Mapping[str, MyType1]
这仍然给了我
NameError: name 'MyType2' is not defined
我知道使用字符串文字的后备语法,并且它确实有效。然而,我很好奇是否有可能使用正式可用的新方法。
你 absolutely 可以使用 Python 3.7+ 中的 postponed evaluation of annotations 来实现这一点,而无需 resorting to string literals。关键在于使用 Python 3.10 中引入的
typing.Self
类型。
from __future__ import annotations
import typing
MyType1 = typing.Union[str, 'MyType2']
MyType2 = typing.Mapping[str, MyType1]
# 或者,从 Python 3.10 开始:
MyType1 = typing.Union[str, typing.Self]
MyType2 = typing.Mapping[str, MyType1]
让我们来分析一下为何这可行:
-
Postponed Evaluation:
from __future__ import annotations
指令告知 Python 解释器不要在函数和类定义中立即计算类型注解。相反,它将它们存储为字符串,并在稍后需要时进行计算。这允许我们使用在定义时尚未完全定义的类型,比如在你的示例中的MyType2
。 -
typing.Self
(Python 3.10+) :typing.Self
是一个特殊的类型提示,它在类定义的上下文中引用“当前类”。通过将typing.Self
用于MyType1
的定义,我们指示类型检查器,MyType1
可以包含对其自身类型的引用,从而有效地解决了循环依赖问题。 -
Strings (Python 3.7 - 3.9) : 在 3.10 之前,你必须使用字符串字面量(如 'MyType2')来引用尚未定义的类型。这种方法仍然有效,但
typing.Self
提供了一个更清晰、更类型安全的解决方案。
通过使用这些功能,你可以创建具有循环引用的复杂类型别名,而不会遇到
NameError
。