TL; DR : se stai usando Python 4.0 funziona. A partire da oggi (2019) in 3.7+ devi attivare questa funzione usando un'istruzione futura ( from __future__ import annotations
) - per Python 3.6 o versioni precedenti usa una stringa.
Immagino tu abbia questa eccezione:
NameError: name 'Position' is not defined
Questo perché Position
deve essere definito prima di poterlo utilizzare in un'annotazione a meno che non si stia utilizzando Python 4.
Python 3.7+: from __future__ import annotations
Python 3.7 introduce PEP 563: valutazione posticipata delle annotazioni . Un modulo che utilizza la dichiarazione futura from __future__ import annotations
memorizzerà automaticamente le annotazioni come stringhe:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Questo è programmato per diventare il predefinito in Python 4.0. Poiché Python è ancora un linguaggio tipizzato in modo dinamico, quindi non viene eseguito alcun controllo del tipo in fase di esecuzione, le annotazioni di digitazione non dovrebbero avere alcun impatto sulle prestazioni, giusto? Sbagliato! Prima di Python 3.7 il modulo di digitazione era uno dei moduli Python più lenti nel core, quindi se import typing
vedrai un aumento delle prestazioni fino a 7 volte quando esegui l'aggiornamento a 3.7.
Python <3.7: usa una stringa
Secondo PEP 484 , è necessario utilizzare una stringa anziché la classe stessa:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Se si utilizza il framework Django, ciò può essere familiare in quanto i modelli Django usano anche le stringhe per riferimenti in avanti (definizioni di chiave esterna in cui il modello esterno è self
o non è ancora stato dichiarato). Questo dovrebbe funzionare con Pycharm e altri strumenti.
fonti
Le parti rilevanti di PEP 484 e PEP 563 , per risparmiare il viaggio:
Riferimenti in avanti
Quando un suggerimento sul tipo contiene nomi che non sono stati ancora definiti, tale definizione può essere espressa come una stringa letterale, da risolvere in seguito.
Una situazione in cui ciò si verifica comunemente è la definizione di una classe contenitore, in cui la classe da definire si verifica nella firma di alcuni dei metodi. Ad esempio, il seguente codice (l'inizio di una semplice implementazione dell'albero binario) non funziona:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
Per affrontare questo, scriviamo:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right
Il valore letterale stringa deve contenere un'espressione Python valida (ovvero compilare (acceso, '', 'eval') dovrebbe essere un oggetto codice valido) e dovrebbe valutare senza errori una volta che il modulo è stato caricato completamente. Lo spazio dei nomi locale e globale in cui viene valutato dovrebbe essere lo stesso spazio dei nomi in cui verrebbero valutati gli argomenti predefiniti della stessa funzione.
e PEP 563:
In Python 4.0, le annotazioni di funzioni e variabili non saranno più valutate al momento della definizione. Invece, una forma di stringa verrà conservata nel rispettivo __annotations__
dizionario. I controllori di tipi statici non vedranno alcuna differenza nel comportamento, mentre gli strumenti che utilizzano le annotazioni in fase di esecuzione dovranno eseguire una valutazione posticipata.
...
Le funzionalità sopra descritte possono essere abilitate a partire da Python 3.7 utilizzando la seguente importazione speciale:
from __future__ import annotations
Cose che potresti essere tentato di fare invece
A. Definisci un manichino Position
Prima della definizione della classe, inserisci una definizione fittizia:
class Position(object):
pass
class Position(object):
...
Questo eliminerà il NameError
e potrebbe anche sembrare OK:
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
Ma è?
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: False
other is Position: False
B. Monkey-patch per aggiungere le annotazioni:
Potresti provare un po 'di magia della meta-programmazione Python e scrivere un decoratore per correggere la definizione della classe in modo da aggiungere annotazioni:
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
Il decoratore dovrebbe essere responsabile dell'equivalente di questo:
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
Almeno sembra giusto:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
Probabilmente troppi problemi.
Conclusione
Se stai usando 3.6 o sotto usa una stringa letterale contenente il nome della classe, in 3.7 usa from __future__ import annotations
e funzionerà.