Storia della domanda:
La memoizzazione è una tecnica di ottimizzazione ampiamente utilizzata nella programmazione funzionale, nei calcoli ricorsivi e nella programmazione dinamica, quando le stesse funzioni vengono chiamate ripetutamente con gli stessi argomenti. In Python è entrata in uso con l'arrivo del decoratore functools.lru_cache e dei modelli di memorizzazione dei risultati dei calcoli.
Problema:
Senza la memorizzazione, le funzioni ricorsive, come il calcolo dei numeri di Fibonacci, funzionano molto lentamente: per ogni argomento, i calcoli vengono ripetuti molte volte, portando a una crescita esponenziale nel numero di chiamate.
Soluzione:
La memoizzazione memorizza i valori già calcolati (ad esempio, in un dizionario) per riutilizzarli quando necessario:
Esempio di implementazione manuale:
memo = {} def fib(n): if n in memo: return memo[n] if n <= 1: result = n else: result = fib(n-1) + fib(n-2) memo[n] = result return result
Con Python 3.2 è possibile utilizzare semplicemente il decoratore:
from functools import lru_cache @lru_cache(maxsize=128) def fib(n): if n <= 1: return n return fib(n-1) + fib(n-2)
Caratteristiche chiave:
È possibile memoizzare una funzione con argomenti mutabili (ad esempio, liste)?
No, lru_cache richiede che tutti gli argomenti siano hashabili (immutabili), altrimenti si ottiene TypeError.
from functools import lru_cache @lru_cache # def f(a): return a[0] # TypeError: tipo non hashable: 'lista'
I valori memoizzati vengono rimossi automaticamente?
Sì, se viene impostato maxsize, i valori che superano il limite vengono rimossi (Least Recently Used). Una cache infinita (maxsize=None) può portare a un overflow di memoria.
Cosa succede se una funzione memoizzata si richiama da sola con nuovi argomenti?
La memoizzazione funzionerà comunque: per ogni insieme unico di argomenti, il risultato viene calcolato solo una volta e memorizzato.
Si scrive una propria cache senza limitare la dimensione:
cache = {} def foo(x): if x not in cache: cache[x] = long_heavy_computation(x) return cache[x]
dopo un po' il programma utilizza gigabyte di memoria.
Vantaggi:
Svantaggi:
Utilizzo di lru_cache con maxsize:
from functools import lru_cache @lru_cache(maxsize=1000) def foo(x): return long_heavy_computation(x)
Vantaggi:
Svantaggi: