TL; DR : si vous utilisez Python 4.0, cela fonctionne. À compter d'aujourd'hui (2019) dans la version 3.7+, vous devez activer cette fonctionnalité à l'aide d'une future déclaration ( from __future__ import annotations
) - pour Python 3.6 ou inférieur, utilisez une chaîne.
Je suppose que vous avez cette exception:
NameError: name 'Position' is not defined
En effet, vous Position
devez le définir avant de pouvoir l'utiliser dans une annotation, sauf si vous utilisez Python 4.
Python 3.7+: from __future__ import annotations
Python 3.7 introduit PEP 563: évaluation différée des annotations . Un module qui utilise la future instruction from __future__ import annotations
stockera automatiquement les annotations sous forme de chaînes:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Il est prévu que cela devienne la valeur par défaut dans Python 4.0. Étant donné que Python est toujours un langage typé dynamiquement, aucune vérification de type n'est donc effectuée au moment de l'exécution, les annotations de frappe ne devraient pas avoir d'impact sur les performances, non? Faux! Avant python 3.7, le module de typage était l' un des modules python les plus lents du cœur, donc si vous import typing
constaterez une augmentation des performances jusqu'à 7 fois lors de la mise à niveau vers la version 3.7.
Python <3.7: utilisez une chaîne
Selon PEP 484 , vous devez utiliser une chaîne au lieu de la classe elle-même:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Si vous utilisez le framework Django, cela peut être familier car les modèles Django utilisent également des chaînes pour les références directes (définitions de clés étrangères où le modèle étranger est self
ou n'est pas encore déclaré). Cela devrait fonctionner avec Pycharm et d'autres outils.
Sources
Les pièces pertinentes du PEP 484 et PEP 563 , pour vous épargner le voyage:
Références avancées
Lorsqu'un indice de type contient des noms qui n'ont pas encore été définis, cette définition peut être exprimée sous la forme d'un littéral de chaîne, à résoudre ultérieurement.
Une situation où cela se produit couramment est la définition d'une classe de conteneur, où la classe en cours de définition se produit dans la signature de certaines des méthodes. Par exemple, le code suivant (le début d'une implémentation d'arbre binaire simple) ne fonctionne pas:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
Pour y remédier, nous écrivons:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right
Le littéral de chaîne doit contenir une expression Python valide (c'est-à-dire que compiler (allumé, '', 'eval') doit être un objet de code valide) et il doit être évalué sans erreur une fois le module entièrement chargé. Les espaces de noms locaux et globaux dans lesquels il est évalué doivent être les mêmes espaces de noms dans lesquels les arguments par défaut de la même fonction seraient évalués.
et PEP 563:
Dans Python 4.0, les annotations de fonction et de variable ne seront plus évaluées au moment de la définition. Au lieu de cela, une forme de chaîne sera conservée dans le __annotations__
dictionnaire respectif . Les vérificateurs de type statique ne verront aucune différence de comportement, tandis que les outils utilisant des annotations lors de l'exécution devront effectuer une évaluation différée.
...
La fonctionnalité décrite ci-dessus peut être activée à partir de Python 3.7 à l'aide de l'importation spéciale suivante:
from __future__ import annotations
Des choses que vous pourriez être tenté de faire à la place
A. Définir un mannequin Position
Avant la définition de classe, placez une définition fictive:
class Position(object):
pass
class Position(object):
...
Cela supprimera le NameError
et peut même sembler OK:
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
Mais est-ce?
>>> 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 pour ajouter les annotations:
Vous voudrez peut-être essayer un peu de magie de méta-programmation Python et écrire un décorateur pour patcher la définition de la classe afin d'ajouter des annotations:
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
Le décorateur devrait être responsable de l'équivalent de ceci:
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
Au moins, cela semble juste:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
Probablement trop de problèmes.
Conclusion
Si vous utilisez la version 3.6 ou inférieure, utilisez un littéral de chaîne contenant le nom de la classe, utilisez la version 3.7 from __future__ import annotations
et cela fonctionnera.