TL; DR : jeśli używasz Python 4.0, to po prostu działa. Na dzień dzisiejszy (2019) w wersji 3.7+ musisz włączyć tę funkcję, używając przyszłej instrukcji ( from __future__ import annotations
) - w Pythonie 3.6 lub niższym użyj ciągu znaków.
Myślę, że masz ten wyjątek:
NameError: name 'Position' is not defined
Wynika to z faktu, że Position
należy go zdefiniować, zanim będzie można go użyć w adnotacji, chyba że używa się języka Python 4.
Python 3.7+: from __future__ import annotations
Python 3.7 wprowadza PEP 563: odroczona ocena adnotacji . Moduł korzystający z przyszłej instrukcji from __future__ import annotations
automatycznie zapisuje adnotacje jako ciągi znaków:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Jest to zaplanowane jako domyślne w Pythonie 4.0. Ponieważ Python nadal jest językiem o dynamicznym pisaniu, więc sprawdzanie typów nie jest wykonywane w czasie wykonywania, adnotacje podczas pisania nie powinny mieć wpływu na wydajność, prawda? Źle! Przed Pythonie 3.7 modułu wpisując kiedyś jeden z najwolniejszych modułów Python w rdzeniu więc jeśli was import typing
widać aż do 7-krotny wzrost wydajności podczas aktualizacji do 3.7.
Python <3.7: użyj ciągu
Według PEP 484 powinieneś użyć ciągu zamiast samej klasy:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Jeśli używasz frameworka Django, może to być znane, ponieważ modele Django używają również ciągów dla referencji do przodu (definicje kluczy obcych, w których model obcy jest self
lub nie został jeszcze zadeklarowany). Powinno to działać z Pycharmem i innymi narzędziami.
Źródła
Odpowiednie części PEP 484 i PEP 563 , aby oszczędzić ci podróży:
Prześlij referencje
Gdy podpowiedź typu zawiera nazwy, które nie zostały jeszcze zdefiniowane, definicję tę można wyrazić dosłownie jako ciąg znaków, co zostanie rozwiązane później.
Sytuacja, w której zdarza się to często, to definicja klasy kontenera, w której definiowana klasa występuje w sygnaturze niektórych metod. Na przykład następujący kod (początek prostej implementacji drzewa binarnego) nie działa:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
Aby rozwiązać ten problem, piszemy:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right
Literał łańcuchowy powinien zawierać prawidłowe wyrażenie w języku Python (tj. Kompilacja (lit, '', 'eval') powinna być prawidłowym obiektem kodu) i powinna być oceniana bez błędów po pełnym załadowaniu modułu. Lokalna i globalna przestrzeń nazw, w której jest obliczana, powinny być tymi samymi przestrzeniami nazw, w których oceniane byłyby domyślne argumenty tej samej funkcji.
i PEP 563:
W Pythonie 4.0 adnotacje funkcji i zmiennych nie będą już oceniane w czasie definiowania. Zamiast tego w odpowiednim __annotations__
słowniku zachowana zostanie forma łańcucha . Mechanizmy sprawdzania typu statycznego nie zauważą żadnej różnicy w zachowaniu, podczas gdy narzędzia używające adnotacji w czasie wykonywania będą musiały wykonać odroczoną ocenę.
...
Funkcję opisaną powyżej można włączyć począwszy od Pythona 3.7, korzystając z następującego specjalnego importu:
from __future__ import annotations
Rzeczy, które możesz pokusić się zamiast tego
A. Zdefiniuj manekina Position
Przed definicją klasy umieść fikcyjną definicję:
class Position(object):
pass
class Position(object):
...
Spowoduje to usunięcie NameError
i może nawet wyglądać OK:
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
Ale czy to jest?
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: False
other is Position: False
B. Łatka-małpa w celu dodania adnotacji:
Możesz wypróbować magię programowania w języku Python i napisać dekorator, aby załatać definicję klasy w celu dodania adnotacji:
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
Dekorator powinien być odpowiedzialny za równowartość tego:
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
Przynajmniej wydaje się słuszne:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
Prawdopodobnie za dużo kłopotów.
Wniosek
Jeśli używasz wersji 3.6 lub niższej, użyj literału łańcuchowego zawierającego nazwę klasy, w wersji 3.7 użyj from __future__ import annotations
i to po prostu zadziała.