Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

191
Views
¿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 answers
Answer question

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

over 4 years ago · Santiago Trujillo Report

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 Report

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 Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!