Wie kann ich angeben, dass der Rückgabetyp einer Methode die gleiche ist wie die Klasse selbst in Python?

Ich habe den folgenden Code in python 3:

class Position: def __init__(self, x: int, y: int): self.x = x self.y = y def __add__(self, other: Position) -> Position: return Position(self.x + other.x, self.y + other.y) 

Aber mein Redakteur (PyCharm) sagt, dass die Referenzposition nicht aufgelöst werden kann (in der _add__-Methode). Wie soll ich angeben, dass ich erwarte, dass der Rückgabetyp vom Typ Position ?

Edit: Ich denke, das ist eigentlich ein PyCharm Problem. Es verwendet tatsächlich die Informationen in seinen Warnungen und Code-Fertigstellung

Aber korrigieren Sie mich, wenn ich falsch liege und eine andere Syntax verwenden muss.

2 Solutions collect form web for “Wie kann ich angeben, dass der Rückgabetyp einer Methode die gleiche ist wie die Klasse selbst in Python?”

Wenn Sie versuchen, diesen Code auszuführen, erhalten Sie:

 NameError: name 'Position' is not defined 

Dies ist, weil Position muss definiert werden, bevor Sie es auf einer Annotation verwenden können. Ich werde durch die Workarounds für ähnliche Fragen vorgeschlagen, wenn Sie in Eile sind "The Blessed Way" ™ ist die # 2.

1. Definiere eine Dummy- Position

Vor der Klassendefinition legen Sie eine Dummy-Definition:

 class Position(object): pass class Position(object): ... 

Dies wird den NameError loswerden 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 

2. Verwenden Sie einen String

Verwenden Sie einfach einen String anstelle der Klasse selbst:

 ... def __add__(self, other: 'Position') -> 'Position': ... 

Wenn Sie das Django-Framework verwenden, kann dies wohl bekannt sein, da Django-Modelle Strings für Vorwärtsreferenzen verwenden (Fremdschlüsseldefinitionen, bei denen das Fremdmodell self oder noch nicht deklariert ist).

Sieht aus wie das ist der empfohlene Ansatz nach den docs :

Vorwärts referenzen

Wenn ein Typ-Hinweis Namen enthält, die noch nicht definiert wurden, kann diese Definition als String-Literal ausgedrückt werden, um später aufgelöst zu werden.

Eine Situation, in der dies häufig vorkommt, ist die Definition einer Containerklasse, wobei die Klasse, die definiert wird, in der Signatur einiger Methoden auftritt. Zum Beispiel funktioniert der folgende Code (der Beginn einer einfachen Binärbaumimplementierung) nicht:

 class Tree: def __init__(self, left: Tree, right: Tree): self.left = left self.right = right 

Um dies zu adressieren, 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 kompilieren (lit, '', 'eval') sollte ein gültiges Codeobjekt sein) und es sollte ohne Fehler ausgewertet werden, sobald das Modul vollständig geladen ist. Der lokale und globale Namespace, in dem es ausgewertet wird, sollte die gleichen Namespaces sein, in denen Standardargumente auf dieselbe Funktion ausgewertet werden sollen.

3. Monkey-Patch, um die Annotationen hinzuzufügen:

Sie können dies verwenden, wenn Sie einen Dekorateur verwenden, der Verträge erzwingt:

 ... 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 

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 

Aber darüber nachzudenken, wenn Sie klug genug sind, um einen Vertrag zu erzwingen Dekorateur, können Sie wahrscheinlich lehren, um sicher zu eval die Annotation, wenn es von Typ buitins.str statt der builtins.type .

Schlussfolgerung

Der gesegnete Weg ist die # 2.

Der Name 'Position' ist zu diesem Zeitpunkt nicht zugänglich, wenn der Klassenkörper selbst analysiert wird. Ich weiß nicht, wie Sie die Typendeklarationen verwenden, aber Python's PEP 484 – was ist das, was der meisten Modus verwenden sollte, wenn Sie diese Tipphinweise verwenden, sagen Sie, dass Sie einfach den Namen als String an dieser Stelle setzen können:

 def __add__(self, other: 'Position') -> 'Position': return Position(self.x + other.x, self.y + other.y) 

Überprüfen Sie https://www.python.org/dev/peps/pep-0484/#forward-references – Werkzeuge, die dem entsprechen, wissen, dass Sie den Klassennamen von dort auspacken und ihn nutzen können. (Es ist immer wichtig zu haben In der Erwägung, dass die Python-Sprache selbst nichts von diesen Annotationen tut – sie sind in der Regel für die statische Codeanalyse gedacht, oder man könnte eine Bibliothek / Framework für die Typprüfung in der Laufzeit haben – aber das muss man explizit setzen)

Python ist die beste Programmiersprache der Welt.