TL; DR : если вы используете Python 4.0, он просто работает. С сегодняшнего дня (2019) в 3.7+ вы должны включить эту функцию, используя оператор future ( from __future__ import annotations
) - для Python 3.6 или ниже используйте строку.
Я думаю, вы получили это исключение:
NameError: name 'Position' is not defined
Это потому, что Position
необходимо определить, прежде чем вы сможете использовать его в аннотации, если вы не используете Python 4.
Python 3.7+: from __future__ import annotations
Python 3.7 представляет PEP 563: отложенная оценка аннотаций . Модуль, который использует оператор future, from __future__ import annotations
будет автоматически сохранять аннотации в виде строк:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Это запланировано, чтобы стать по умолчанию в Python 4.0. Поскольку Python по-прежнему является языком с динамической типизацией, поэтому проверка типов во время выполнения не выполняется, ввод аннотаций не должен влиять на производительность, верно? Неправильно! Перед тем как питон 3.7, модуль типирование используется как один из самых медленных питона модулей в ядре , так если вы import typing
увидите до 7 - кратного увеличения производительности при обновлении до 3.7.
Python <3.7: использовать строку
Согласно PEP 484 , вы должны использовать строку вместо самого класса:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Если вы используете инфраструктуру Django, это может быть знакомо, поскольку модели Django также используют строки для прямых ссылок (определения внешнего ключа, где внешняя модель еще объявлена self
или еще не объявлена). Это должно работать с Pycharm и другими инструментами.
источники
Соответствующие части PEP 484 и PEP 563 , чтобы избавить вас от поездки:
Прямые ссылки
Когда подсказка типа содержит имена, которые еще не определены, это определение может быть выражено как строковый литерал, который будет решен позже.
Ситуация, в которой это обычно происходит, - это определение класса контейнера, где определяемый класс встречается в сигнатуре некоторых методов. Например, следующий код (начало реализации простого двоичного дерева) не работает:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
Для решения этой проблемы мы пишем:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right
Строковый литерал должен содержать допустимое выражение Python (т. Е. Compile (lit, '', 'eval') должен быть допустимым объектом кода), и он должен вычисляться без ошибок после полной загрузки модуля. Локальное и глобальное пространство имен, в котором оно оценивается, должно быть теми же пространствами имен, в которых будут оцениваться аргументы по умолчанию для одной и той же функции.
и PEP 563:
В Python 4.0 аннотации функций и переменных больше не будут оцениваться во время определения. Вместо этого строковая форма будет сохранена в соответствующем __annotations__
словаре. Проверщики статического типа не увидят никакой разницы в поведении, тогда как инструменты, использующие аннотации во время выполнения, должны будут отложить оценку.
...
Описанную выше функциональность можно включить начиная с Python 3.7, используя следующий специальный импорт:
from __future__ import annotations
Вещи, которые вы можете испытать вместо этого
А. Определить манекен Position
Перед определением класса поместите фиктивное определение:
class Position(object):
pass
class Position(object):
...
Это избавит от NameError
и может даже выглядеть хорошо:
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
Но так ли это?
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: False
other is Position: False
Б. Обезьяна-патч для добавления аннотаций:
Возможно, вы захотите попробовать магию метапрограммирования на Python и написать декоратор, который исправит определение класса, чтобы добавить аннотации:
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
Декоратор должен нести ответственность за эквивалент этого:
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
По крайней мере, это кажется правильным:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
Вероятно, слишком много проблем.
Вывод
Если вы используете 3.6 или ниже, используйте строковый литерал, содержащий имя класса, в 3.7 используйте, from __future__ import annotations
и это будет просто работать.