As a toy example, let's use the Fibonacci sequence:
def fib(n: int) -> int:
if n < 2:
return 1
return fib(n - 2) + fib(n - 1)
Of course, this will hang the computer if we try to:
print(fib(100))
So we decide to add memoization. To keep the logic of fib clear, we decide not to change fib and instead add memoization via a decorator:
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)
Now there is no problem if we:
print(fib(100))
573147844013817084101
However, mypy complains that "Callable[[int], int]" has no attribute "memory", which makes sense, and usually I would want this complaint if I tried to access a property that is not part of the declared type...
So, how should we use typing to indicate that wrapper, while a Callable, also has the property memory?