privados - metodos estáticos python
¿Cómo especifico que el tipo de retorno de un método es el mismo que la clase misma? (3)
El nombre ''Posición'' no está disponible en el momento en que se analiza el cuerpo de la clase. No sé cómo está utilizando las declaraciones de tipo, pero el PEP 484 de Python, que es lo que la mayoría del modo debería usar si usa estas sugerencias de escritura, dice que simplemente puede poner el nombre como una cadena en este punto:
def __add__(self, other: ''Position'') -> ''Position'':
return Position(self.x + other.x, self.y + other.y)
Verifique https://www.python.org/dev/peps/pep-0484/#forward-references : las herramientas que se ajusten a eso sabrán desenvolver el nombre de la clase desde allí y usarlo. (Siempre es importante tener Tenga en cuenta que el lenguaje Python en sí mismo no hace nada de estas anotaciones, generalmente están destinadas al análisis de código estático, o uno podría tener una biblioteca / marco para la verificación de tipos en tiempo de ejecución, pero debe establecerlo explícitamente)
Tengo el siguiente código en 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)
Pero mi editor (PyCharm) dice que la posición de referencia no se puede resolver (en el método
__add__
).
¿Cómo debo especificar que espero que el tipo de retorno sea del tipo
Position
?
Editar: Creo que esto es realmente un problema de PyCharm. Realmente usa la información en sus advertencias y la finalización del código
Pero corrígeme si me equivoco y necesito usar otra sintaxis.
Especificar el tipo como cadena está bien, pero siempre me molesta un poco que básicamente estemos eludiendo el analizador. Por lo tanto, es mejor que no escriba mal ninguna de estas cadenas literales:
def __add__(self, other: ''Position'') -> ''Position'':
return Position(self.x + other.x, self.y + other.y)
Una ligera variación es usar un typevar encuadernado, al menos entonces debe escribir la cadena solo una vez al declarar el typevar:
from typing import TypeVar
T = TypeVar(''T'', bound=''Position'')
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self, other: T) -> T:
return Position(self.x + other.x, self.y + other.y)
TL; DR
: si está utilizando Python 4.0, simplemente funciona.
A partir de hoy (2019) en 3.7+, debe activar esta función utilizando 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 debe a que la
Position
debe definirse antes de que pueda usarla 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á las 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 que si
import typing
, 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 es
self
o aún no se ha declarado).
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 diccionario
__annotations__
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. Definir una
Position
ficticia
Antes de la definición de clase, coloque una definición ficticia:
class Position(object):
pass
class Position(object):
...
Esto eliminará el
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 algo de la 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, en 3.7 use las
from __future__ import annotations
y simplemente funcionará.