TL; DR : si está utilizando Python 4.0, simplemente funciona. A partir de hoy (2019) en 3.7+, debe activar esta función usando una declaración futura ( from __future__ import annotations
); para Python 3.6 o inferior, use una cadena.
Supongo que tienes esta excepción:
NameError: name 'Position' is not defined
Esto se Position
debe a que debe definirse antes de poder usarlo en una anotación a menos que esté usando Python 4.
Python 3.7+: from __future__ import annotations
Python 3.7 presenta PEP 563: evaluación pospuesta de anotaciones . Un módulo que usa la declaración futura from __future__ import annotations
almacenará anotaciones como cadenas automáticamente:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
Esto está programado para convertirse en el predeterminado en Python 4.0. Dado que Python todavía es un lenguaje de tipo dinámico, por lo que no se realiza ninguna verificación de tipo en tiempo de ejecución, las anotaciones de escritura no deberían tener un impacto en el rendimiento, ¿verdad? ¡Incorrecto! Antes de Python 3.7, el módulo de mecanografía solía ser uno de los módulos de Python más lentos en el núcleo, por lo import typing
que si verá un aumento de rendimiento de hasta 7 veces cuando actualice a 3.7.
Python <3.7: usa una cadena
De acuerdo con PEP 484 , debe usar una cadena en lugar de la clase en sí:
class Position:
...
def __add__(self, other: 'Position') -> 'Position':
...
Si usa el marco de Django, esto puede resultarle familiar, ya que los modelos de Django también usan cadenas para referencias directas (definiciones de clave externa donde el modelo externo está self
o no declarado aún). Esto debería funcionar con Pycharm y otras herramientas.
Fuentes
Las partes relevantes de PEP 484 y PEP 563 , para ahorrarle el viaje:
Reenviar referencias
Cuando una sugerencia de tipo contiene nombres que aún no se han definido, esa definición puede expresarse como un literal de cadena, que se resolverá más adelante.
Una situación en la que esto ocurre comúnmente es la definición de una clase contenedor, donde la clase que se define ocurre en la firma de algunos de los métodos. Por ejemplo, el siguiente código (el inicio de una implementación de árbol binario simple) no funciona:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
Para abordar esto, escribimos:
class Tree:
def __init__(self, left: 'Tree', right: 'Tree'):
self.left = left
self.right = right
El literal de cadena debe contener una expresión Python válida (es decir, compilar (iluminado, '', 'evaluar') debe ser un objeto de código válido) y debe evaluar sin errores una vez que el módulo se haya cargado completamente. El espacio de nombres local y global en el que se evalúa debe ser el mismo espacio de nombres en el que se evaluarían los argumentos predeterminados para la misma función.
y PEP 563:
En Python 4.0, las anotaciones de funciones y variables ya no se evaluarán en el momento de la definición. En cambio, se conservará una forma de cadena en el __annotations__
diccionario respectivo . Los verificadores de tipo estático no verán diferencias en el comportamiento, mientras que las herramientas que usan anotaciones en tiempo de ejecución deberán realizar una evaluación pospuesta.
...
La funcionalidad descrita anteriormente se puede habilitar a partir de Python 3.7 utilizando la siguiente importación especial:
from __future__ import annotations
Cosas que quizás tengas la tentación de hacer
A. Define un muñeco Position
Antes de la definición de clase, coloque una definición ficticia:
class Position(object):
pass
class Position(object):
...
Esto eliminará NameError
e incluso puede verse bien:
>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}
¿Pero 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. Parche de mono para agregar las anotaciones:
Es posible que desee probar un poco de magia de metaprogramación de Python y escribir un decorador para parchear la definición de clase para agregar anotaciones:
class Position:
...
def __add__(self, other):
return self.__class__(self.x + other.x, self.y + other.y)
El decorador debe ser responsable del equivalente de esto:
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
Al menos parece correcto:
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
Probablemente demasiados problemas.
Conclusión
Si está utilizando 3.6 o menos, use un literal de cadena que contenga el nombre de la clase, use 3.7 from __future__ import annotations
y simplemente funcionará.