una privados otra metodos llamar instanciar funciones estáticos dentro como clases clase atributos python python-3.x pycharm typing python-3.5

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