Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

175
Vistas
¿Cómo debemos escribir un invocable con propiedades adicionales?

Como ejemplo de juguete, usemos la secuencia de Fibonacci:

 def fib(n: int) -> int: if n < 2: return 1 return fib(n - 2) + fib(n - 1)

Por supuesto, esto colgará la computadora si intentamos:

 print(fib(100))

Entonces decidimos agregar memorización. Para mantener clara la lógica de fib , decidimos no cambiar fib y en su lugar agregar memoization a través de un decorador:

 from typing import Callable from functools import wraps def remember(f: Callable[[int], int]) -> Callable[[int], int]: @wraps(f) def wrapper(n: int) -> int: if n not in wrapper.memory: wrapper.memory[n] = f(n) return wrapper.memory[n] wrapper.memory = dict[int, int]() return wrapper @remember def fib(n: int) -> int: if n < 2: return 1 return fib(n - 2) + fib(n - 1)

Ahora no hay problema si nosotros:

 print(fib(100))
 573147844013817084101

Sin embargo, mypy se queja de que "Callable[[int], int]" has no attribute "memory" , lo cual tiene sentido, y por lo general querría esta queja si intentara acceder a una propiedad que no forma parte del tipo declarado. .

Entonces, ¿cómo deberíamos usar la typing para indicar que wrapper , mientras que Callable , también tiene la propiedad memory ?

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

Para describir algo como "un invocable con un atributo de memoria" , podría definir un protocolo (Python 3.8+ o versiones anteriores con typing_extensions ):

 from typing import Protocol class Wrapper(Protocol): memory: dict[int, int] def __call__(self, n: int) -> int: ...

En uso, el verificador de tipos sabe que un Wrapper es válido como Callable[[int], int] y permite return wrapper así como la asignación a wrapper.memory :

 from functools import wraps from typing import Callable, cast def remember(f: Callable[[int], int]) -> Callable[[int], int]: @wraps(f) def _wrapper(n: int) -> int: if n not in wrapper.memory: wrapper.memory[n] = f(n) return wrapper.memory[n] wrapper = cast(Wrapper, _wrapper) wrapper.memory = dict() return wrapper

Patio de recreo

Desafortunadamente, esto requiere wrapper = cast(Wrapper, _wrapper) , que no es de tipo seguro; wrapper = cast(Wrapper, "foo") también funcionaría correctamente.

over 4 years ago · Santiago Trujillo Denunciar

0

No use un atributo de función para almacenar el caché y no tendrá este problema. Ya está definiendo un cierre (el wrapper mantiene una referencia al invocable original), así que almacene el caché en el cierre también.

 from typing import Callable from functools import wraps def remember(f: Callable[[int], int]) -> Callable[[int], int]: cache: dict[int, int] = {} @wraps(f) def wrapper(n: int) -> int: if n not in cache: cache[n] = f(n) return cache[n] return wrapper @remember def fib(n: int) -> int: if n < 2: return 1 return fib(n - 2) + fib(n - 1)
over 4 years ago · Santiago Trujillo Denunciar

0

Partiendo de la respuesta de jonrsharpe (que funciona, sugiere lo siguiente y acepté), podemos evitar la necesidad de una conversión no segura de tipos de la siguiente manera:

 from typing import Callable from functools import wraps class Remember: def __init__(self) -> None: self.memory = dict[int, int]() def __call__(self, f: Callable[[int], int]) -> Callable[[int], int]: @wraps(f) def wrapper(n: int) -> int: if n not in self.memory: self.memory[n] = f(n) return self.memory[n] return wrapper @Remember() def fib(n: int) -> int: if n < 2: return 1 return fib(n - 2) + fib(n - 1) print(fib(100))
over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda