Использование класса в качестве типа подсказки для аргументов в его методах

В приведенном ниже коде приведена следующая ошибка:

NameError: name 'Vector2' is not defined 

в этой строке:

def Translate (self, pos: Vector2):

Почему Python не распознает мой класс Vector2 в методе 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

Ответ 1

Поскольку, когда он встречает Translate (при компиляции тела класса), Vector2 еще не был определен (в настоящее время он компилируется, привязка имени не была выполнена); Питон естественно жалуется.

Поскольку это такой распространенный сценарий (намеки на тип класса в теле этого класса), вы должны использовать прямую ссылку на него, заключив его в кавычки:

class Vector2:    
    # __init__ as defined

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

Python (и любые контролеры, соответствующие PEP 484) поймут вашу подсказку и зарегистрируют ее соответствующим образом. Python распознает это, когда к __annotations__ обращаются через typing.get_type_hints:

from typing import get_type_hints

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

Это было изменено с Python 3.7; см. ответ abarnert ниже.

Ответ 2

Функция, которую вы запрашиваете, называется прямой (типовой) ссылкой, и она была добавлена в Python с версии 3.7 (в PEP 563). 1 Так что теперь это действительно так:

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

Обратите внимание на утверждение __future__. Это будет необходимо до версии 4.0.

К сожалению, в Python 3.6 и более ранних версиях эта функция недоступна, поэтому вы должны использовать строковые аннотации, как описано в ответе Джима Фасаракиса Хиллиарда.

Mypy уже поддерживает предварительные объявления, даже при запуске под Python 3.6 - но это не принесет вам большой пользы, если средство проверки статического типа говорит, что ваш код в порядке, но интерпретатор вызывает NameError, когда вы пытаетесь запустить его на самом деле.


1. Это уже обсуждалось в качестве возможной функции в PEP 484, но откладывалось на потом, после того, как у людей появилось больше опыта использования предварительных объявлений в аннотациях. PEP 563/Python 3.7 - это "позже".