TL; DR : якщо ви використовуєте Python 4.0, він просто працює. На сьогодні (2019) у версії 3.7+ ви повинні увімкнути цю функцію за допомогою майбутнього оператора ( 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: відкладене оцінювання приміток . Модуль, який використовує майбутній оператор, from __future__ import annotations
автоматично зберігатиме анотації як рядки:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Це планується стати типовим у Python 4.0. Оскільки Python все ще є динамічно набраною мовою, тому перевірка типу не виконується під час виконання, введення анотацій не повинно впливати на ефективність, правда? Неправильно! Перед python 3.7 модуль набору тексту був одним із найповільніших модулів python в ядрі, тож якщо ви import typing
побачите, що під час оновлення до 3.7 ви збільшите продуктивність до 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 (тобто компіляція (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
B. Патч мавп, щоб додати примітки:
Ви можете спробувати трохи магії програмування 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
це буде просто працювати.