类型注释中未定义名称

70

我目前正在创建一个用于个人娱乐和练习语言技能的python线性代数模块。最近,我尝试为该模块添加类型注释,如下所示:

class Vector:
     # Various irrelevant implementation details
     def __add__(self, other: Vector) -> Vector:
        # More implementation details....

然而,当我试图导入它时,它会抛出一个 NameError: Name 'Vector' is not defined 的错误。我知道这个问题已经在这里以某种形式被回答过了:here,但它似乎并没有完全为我的情况提供答案。

我想知道:

  • 我已经在这个文件中明确定义了这个类,为什么它说这个名称未定义?
  • 如何定义 Vector,使其可以用作注释(作为一个 type)?

请注意,每个帖子只限于一个问题。您在此处有两个不同的问题,一个是关于循环依赖(类的方法依赖于在其之前创建的类),另一个是类型和类之间的区别。后者是重复的。 - Martijn Pieters
请参阅 Python 中的类与类型 以获取您在帖子中删除的部分的问题。 - Martijn Pieters
抱歉,我认为类型问题与手头的问题有关。 - MutantOctopus
2个回答

108

你有一个前向声明;将作为方法绑定的函数是在类之前创建的,因此名称Vector还不存在。只有当整个类体被执行完毕时,Python才能创建class对象并将名称Vector绑定到它上面。

只需使用字符串代替名称即可:

class Vector:
     # Various irrelevant implementation details
     def __add__(self, other: 'Vector') -> 'Vector':
        # More implementation details....

这不会影响你的IDE对声明的看法;字符串在整个模块被加载后进行查找,并且在当前上下文中将其解析为有效的Python表达式。由于类Vector在整个模块被加载后存在,因此字符串'Vector'可以正确地转换为类对象。

另请参阅前向引用的规范说明

当类型提示包含尚未定义的名称时,该定义可以表示为字符串字面量,以供稍后解决。

[...]

字符串文字应包含有效的Python表达式[...],并且一旦完全加载了模块,它应该能够无错误地评估。

从Python 3.7开始,您可以通过在模块顶部添加from __future__ import annotations指示来使给定模块中的所有注释都像前向注释一样运行(无需将其包含在字符串字面量中)。最初计划在Python 3.10及以上版本中将其设置为默认值,但现在已经无限期地推迟了这个决定。有关详细信息,请参见PEP 563 -- 注释的推迟评估。请注意,在注释之外,您可能仍然需要使用前向引用语法(字符串文字),例如在类型别名中(在Python看来,这是常规变量赋值)。


56

如果你使用的是 Python 3.7 及以上版本,请查看注解的延迟评估

自 Python 3.7 开始,你只需要增加以下代码即可:

from __future__ import annotations

还要注意的是

它将成为Python 3.10中的默认设置。


2
这很有趣。我将保留原样作为答案,因为它适用于所有3.x版本(或者至少比“import annotations”解决方案适用的版本更多),但我仍然很高兴你在这里发布了这个问题。 - MutantOctopus
请确保将此行作为模块的第一行输入。 - Vlad Bezden
@VladBezden 当然,就像 pep8 建议的那样:导入语句放在文件顶部,而 future 的导入应该最先进行。 - vishes_shell
对于Python 3.6,我遇到了错误“未定义future feature annotations”。 - Shital Shah
@ShitalShah 是的,因为这是 Python 3.7.x - 4.0 的一个功能,正如答案中所说的。 - vishes_shell
这似乎是一个更加规范的解决方案。 - Foad S. Farimani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接