usan tipos propias print lista las guardar globales funciones como clases python python-3.x annotations type-hinting

python - tipos - ¿Por qué el nombre de la clase contenedora no se reconoce como una anotación de función de valor de retorno?



tipos de funciones en python (2)

Iba a utilizar las anotaciones de la función Python para especificar el tipo del valor de retorno de un método de fábrica estático. Entiendo que este es uno de los casos de uso deseados para las anotaciones.

class Trie: @staticmethod def from_mapping(mapping) -> Trie: # docstrings and initialization ommitted trie = Trie() return trie

PEP 3107 establece que:

Las anotaciones de funciones no son más que una forma de asociar expresiones de Python arbitrarias con varias partes de una función en tiempo de compilación.

Trie es una expresión válida en Python, ¿no es así? Python no está de acuerdo o, mejor dicho, no puede encontrar el nombre:

def from_mapping(mapping) -> Trie:
NameError: name ''Trie'' is not defined

Vale la pena señalar que este error no ocurre si se especifica un tipo fundamental (como object o int ) o un tipo de biblioteca estándar (como collections.deque ).

¿Qué está causando este error y cómo puedo solucionarlo?


PEP 484 proporciona una solución oficial a esto en forma de referencias futuras .

Cuando una sugerencia de tipo contiene nombres que aún no se han definido, esa definición se puede expresar como una cadena literal, que se resolverá más adelante.

En el caso del código de la pregunta:

class Trie: @staticmethod def from_mapping(mapping) -> Trie: # docstrings and initialization ommitted trie = Trie() return trie

Se convierte en

class Trie: @staticmethod def from_mapping(mapping) -> ''Trie'': # docstrings and initialization ommitted trie = Trie() return trie


Trie es una expresión válida y se evalúa al valor actual asociado con el nombre de Trie . Pero ese nombre aún no está definido: un objeto de clase solo está vinculado a su nombre después de que el cuerpo de la clase se haya ejecutado hasta su finalización. Notarás el mismo comportamiento en este ejemplo mucho más simple:

class C: myself = C # or even just C

Normalmente, la solución alternativa sería establecer el atributo de clase después de que se haya definido la clase, fuera del cuerpo de la clase. Esta no es una opción realmente buena aquí, aunque funciona. Alternativamente, puede usar cualquier valor de marcador de posición en la definición inicial, luego reemplazarlo en las __annotations__ (lo cual es legal porque es un diccionario regular):

class C: def f() -> ...: pass print(C.f.__annotations__) C.f.__annotations__[''return''] = C print(C.f.__annotations__)

Sin embargo, se siente bastante hacky. Dependiendo de su caso de uso, podría ser posible utilizar un objeto centinela (por ejemplo, CONTAINING_CLASS = object() ) y dejar que se interprete lo que realmente procesa las anotaciones.