31

我在下面包含的代码会引发以下错误:

NameError: name 'Vector2' is not defined 

在这一行:

def Translate (self, pos: Vector2):

为什么 PythonVector2在方法中无法识别我的类Translate

class Vector2:

    def __init__(self, x: float, y: float):

        self.x = x
        self.y = y

    def Translate(self, pos: Vector2):

        self.x += pos.x
        self.y += pos.y
4

3 回答 3

37

因为遇到的时候Translate(在编译类体的时候),Vector2还没有定义(目前正在编译,名字绑定还没有进行);Python 自然会抱怨。

由于这是一种常见的情况(在该类的主体中对类进行类型提示),因此您应该通过将其括在引号中来使用对它的前向引用:

class Vector2:    
    # __init__ as defined

    def Translate(self, pos: 'Vector2'):    
        self.x += pos.x
        self.y += pos.y

Python(以及任何符合 的检查器PEP 484)将理解您的提示并适当地注册它。__annotations__当通过以下方式访问时, Python 确实可以识别这一点typing.get_type_hints

from typing import get_type_hints

get_type_hints(Vector2(1,2).Translate)
{'pos': __main__.Vector2}

从 Python 3.7 开始,这已更改;请参阅下面的 abarnert 的答案

于 2016-10-14T17:43:02.333 回答
15

您要求的功能称为前向(类型)引用,它已从 3.7 开始添加到 Python(在PEP 563中)。1所以这现在是有效的:

from __future__ import annotations
class C:
    def spam(self, other: C) -> C:
        pass

注意__future__声明。这将是必要的,直到 4.0

不幸的是,在 Python 3.6 及更早版本中,此功能不可用,因此您必须使用字符串注释,如Jim Fasarakis Hilliard 的回答中所述。

Mypy 已经支持前向声明,即使在 Python 3.6 下运行时也是如此——但如果静态类型检查器说你的代码没问题但解释器NameError在你尝试实际运行它时引发 a ,它对你没有多大好处。


1. 这已经在PEP 484中作为一个可能的特性进行了讨论,但推迟到后来,因为人们在注释中使用前向声明有更多经验。PEP 563/Python 3.7 就是“后来”。

于 2018-06-08T20:42:59.933 回答
0

也许另一种选择是之前定义类,使用空实现。我想最常见的解决方案是前向引用,但我的建议是更加类型安全,这毕竟是添加类型的目的。

class Vector2:
    pass

class Vector2:

    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

    def Translate(self, pos: Vector2):
        self.x += pos.x
        self.y += pos.y
于 2020-01-30T13:41:32.310 回答