TL; DR : Wenn Sie Python 4.0 verwenden, funktioniert es einfach. Ab heute (2019) in Version 3.7+ müssen Sie diese Funktion mithilfe einer zukünftigen Anweisung ( from __future__ import annotations
) aktivieren. Verwenden Sie für Python 3.6 oder niedriger eine Zeichenfolge.
Ich denke du hast diese Ausnahme:
NameError: name 'Position' is not defined
Dies liegt daran, Position
dass definiert werden muss, bevor Sie es in einer Anmerkung verwenden können, es sei denn, Sie verwenden Python 4.
Python 3.7+: from __future__ import annotations
Python 3.7 wird eingeführt PEP 563 ein: Verschiebung der Auswertung von Anmerkungen . Ein Modul, das die zukünftige Anweisung verwendet from __future__ import annotations
, speichert Anmerkungen automatisch als Zeichenfolgen:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Dies soll in Python 4.0 zum Standard werden. Da Python immer noch eine dynamisch typisierte Sprache ist und zur Laufzeit keine Typprüfung durchgeführt wird, sollten Tippanmerkungen keine Auswirkungen auf die Leistung haben, oder? Falsch! Vor Python 3.7 war das Typisierungsmodul eines der langsamsten Python-Module im Kern so , wenn Sie import typing
Sie werden sehen , bis zu 7 - mal in der Leistung zu erhöhen , wenn Sie auf 3,7 aktualisieren.
Python <3.7: Verwenden Sie eine Zeichenfolge
Nach PEP 484 sollten Sie anstelle der Klasse selbst eine Zeichenfolge verwenden:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Wenn Sie das Django-Framework verwenden, ist dies möglicherweise bekannt, da Django-Modelle auch Zeichenfolgen für Vorwärtsreferenzen verwenden (Fremdschlüsseldefinitionen, bei denen das Fremdmodell self
noch deklariert ist oder nicht). Dies sollte mit Pycharm und anderen Tools funktionieren.
Quellen
Die relevanten Teile von PEP 484 und PEP 563 , um Ihnen die Reise zu ersparen:
Referenzen weiterleiten
Wenn ein Typhinweis Namen enthält, die noch nicht definiert wurden, kann diese Definition als Zeichenfolgenliteral ausgedrückt werden, um später aufgelöst zu werden.
Eine Situation, in der dies häufig vorkommt, ist die Definition einer Containerklasse, in der die zu definierende Klasse in der Signatur einiger Methoden vorkommt. Der folgende Code (der Beginn einer einfachen Implementierung eines Binärbaums) funktioniert beispielsweise nicht:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
Um dies zu beheben, schreiben wir:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right
Das String-Literal sollte einen gültigen Python-Ausdruck enthalten (dh compile (lit, '', 'eval') sollte ein gültiges Codeobjekt sein) und nach dem vollständigen Laden des Moduls fehlerfrei ausgewertet werden. Der lokale und globale Namespace, in dem er ausgewertet wird, sollten dieselben Namespaces sein, in denen Standardargumente für dieselbe Funktion ausgewertet werden.
und PEP 563:
In Python 4.0 werden Funktions- und Variablenanmerkungen zur Definitionszeit nicht mehr ausgewertet. Stattdessen wird eine Zeichenfolgenform im jeweiligen __annotations__
Wörterbuch beibehalten . Statische Typprüfungen sehen keinen Unterschied im Verhalten, während Tools, die zur Laufzeit Anmerkungen verwenden, eine verschobene Auswertung durchführen müssen.
...
Die oben beschriebene Funktionalität kann ab Python 3.7 mit dem folgenden speziellen Import aktiviert werden:
from __future__ import annotations
Dinge, zu denen Sie möglicherweise versucht sind
A. Definieren Sie einen Dummy Position
Platzieren Sie vor der Klassendefinition eine Dummy-Definition:
class Position(object):
pass
class Position(object):
...
Dies wird das loswerden NameError
und kann sogar OK aussehen:
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
Aber ist es?
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: False
other is Position: False
B. Affen-Patch, um die Anmerkungen hinzuzufügen:
Vielleicht möchten Sie etwas Python-Meta-Programmiermagie ausprobieren und einen Dekorator schreiben, um die Klassendefinition mit Affen zu patchen, um Anmerkungen hinzuzufügen:
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
Der Dekorateur sollte für das Äquivalent verantwortlich sein:
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
Zumindest scheint es richtig:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
Wahrscheinlich zu viel Ärger.
Fazit
Wenn Sie 3.6 oder niedriger verwenden, verwenden Sie ein Zeichenfolgenliteral, das den Klassennamen enthält. Verwenden Sie in 3.7 from __future__ import annotations
und es funktioniert einfach.