Bild vom Autor
In Python können Sie Caching verwenden, um die Ergebnisse aufwändiger Funktionsaufrufe zu speichern und sie wiederzuverwenden, wenn die Funktion erneut mit denselben Argumenten aufgerufen wird. Dadurch wird Ihr Code leistungsfähiger.
Python bietet integrierte Unterstützung für das Caching durch die functools
Modul: die Dekorateure @cache
Und @lru_cache
. Und wir werden in diesem Tutorial lernen, wie man Funktionsaufrufe zwischenspeichert.
Warum ist Caching hilfreich?
Das Zwischenspeichern von Funktionsaufrufen kann die Leistung Ihres Codes erheblich verbessern. Hier sind einige Gründe, warum das Zwischenspeichern von Funktionsaufrufen von Vorteil sein kann:
- Leistungsverbesserung: Wenn eine Funktion mehrmals mit denselben Argumenten aufgerufen wird, kann das Zwischenspeichern des Ergebnisses redundante Berechnungen vermeiden. Anstatt das Ergebnis jedes Mal neu zu berechnen, kann der zwischengespeicherte Wert zurückgegeben werden, was zu einer schnelleren Ausführung führt.
- Reduzierung des Ressourcenverbrauchs: Einige Funktionsaufrufe können rechenintensiv sein oder erhebliche Ressourcen erfordern (z. B. Datenbankabfragen oder Netzwerkanforderungen). Durch das Zwischenspeichern der Ergebnisse müssen diese Vorgänge nicht so oft wiederholt werden.
- Verbesserte Reaktionsfähigkeit: Bei Anwendungen, bei denen die Reaktionsfähigkeit von entscheidender Bedeutung ist, wie etwa Webservern oder GUI-Anwendungen, kann das Caching zur Reduzierung der Latenz beitragen, indem wiederholte Berechnungen oder E/A-Vorgänge vermieden werden.
Kommen wir nun zum Codieren.
Caching mit dem @cache Decorator
Lassen Sie uns eine Funktion codieren, die die n-te Fibonacci-Zahl berechnet. Hier ist die rekursive Implementierung der Fibonacci-Folge:
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
Ohne Zwischenspeicherung führen die rekursiven Aufrufe zu redundanten Berechnungen. Wenn die Werte zwischengespeichert werden, wäre es viel effizienter, die zwischengespeicherten Werte nachzuschlagen. Und dafür können Sie den @cache
Dekorateur.
Der @cache
Dekorateur aus dem functools
Das Modul in Python 3.9+ wird verwendet, um die Ergebnisse einer Funktion zwischenzuspeichern. Es funktioniert, indem es die Ergebnisse teurer Funktionsaufrufe speichert und sie wiederverwendet, wenn die Funktion mit denselben Argumenten aufgerufen wird. Lassen Sie uns nun die Funktion mit dem @cache
Dekorateur:
from functools import cache
@cache
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
Wir werden später auf den Leistungsvergleich eingehen. Sehen wir uns nun eine andere Möglichkeit an, Rückgabewerte von Funktionen mit dem @lru_cache
Dekorateur.
Zwischenspeichern mit dem @lru_cache Decorator
Sie können die integrierte functools.lru_cache
Dekorator auch für das Caching. Dies verwendet den Least Just lately Used (LRU)-Caching-Mechanismus für Funktionsaufrufe. Wenn beim LRU-Caching der Cache voll ist und ein neues Ingredient hinzugefügt werden muss, wird das am wenigsten zuletzt verwendete Ingredient im Cache entfernt, um Platz für das neue Ingredient zu schaffen. Dadurch wird sichergestellt, dass die am häufigsten verwendeten Elemente im Cache erhalten bleiben, während weniger häufig verwendete Elemente verworfen werden.
Der @lru_cache
Dekorateur ist ähnlich wie @cache
Sie können jedoch die maximale Größe festlegen, da die maxsize
Argument – des Caches. Sobald der Cache diese Größe erreicht, werden die zuletzt verwendeten Elemente verworfen. Dies ist nützlich, wenn Sie die Speichernutzung begrenzen möchten.
Hier das fibonacci
Die Funktion speichert bis zu 7 zuletzt berechnete Werte im Cache:
from functools import lru_cache
@lru_cache(maxsize=7) # Cache as much as 7 most up-to-date outcomes
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
fibonacci(5) # Computes Fibonacci(5) and caches intermediate outcomes
fibonacci(3) # Retrieves Fibonacci(3) from the cache
Hier das fibonacci
Funktion ist dekoriert mit @lru_cache(maxsize=7)
und geben Sie an, dass bis zu 7 aktuelle Ergebnisse zwischengespeichert werden sollen.
Wann fibonacci(5)
heißt, die Ergebnisse für fibonacci(4)
, fibonacci(3)
Und fibonacci(2)
werden zwischengespeichert. Wenn fibonacci(3)
wird anschließend aufgerufen, fibonacci(3)
wird aus dem Cache abgerufen, da es sich um einen der sieben zuletzt berechneten Werte handelte, wodurch redundante Berechnungen vermieden werden.
Timing von Funktionsaufrufen zum Vergleich
Vergleichen wir nun die Ausführungszeiten der Funktionen mit und ohne Caching. Für dieses Beispiel setzen wir keinen expliziten Wert für maxsize
. Additionally maxsize
wird auf den Standardwert 128 gesetzt:
from functools import cache, lru_cache
import timeit
# with out caching
def fibonacci_no_cache(n):
if n <= 1:
return n
return fibonacci_no_cache(n-1) + fibonacci_no_cache(n-2)
# with cache
@cache
def fibonacci_cache(n):
if n <= 1:
return n
return fibonacci_cache(n-1) + fibonacci_cache(n-2)
# with LRU cache
@lru_cache
def fibonacci_lru_cache(n):
if n <= 1:
return n
return fibonacci_lru_cache(n-1) + fibonacci_lru_cache(n-2)
Um die Ausführungszeiten zu vergleichen, verwenden wir die timeit
„ Funktion aus dem timeit
Modul:
# Compute the n-th Fibonacci quantity
n = 35
no_cache_time = timeit.timeit(lambda: fibonacci_no_cache(n), quantity=1)
cache_time = timeit.timeit(lambda: fibonacci_cache(n), quantity=1)
lru_cache_time = timeit.timeit(lambda: fibonacci_lru_cache(n), quantity=1)
print(f"Time with out cache: {no_cache_time:.6f} seconds")
print(f"Time with cache: {cache_time:.6f} seconds")
print(f"Time with LRU cache: {lru_cache_time:.6f} seconds")
Die Ausführung des obigen Codes sollte zu einer ähnlichen Ausgabe führen:
Output >>>
Time with out cache: 2.373220 seconds
Time with cache: 0.000029 seconds
Time with LRU cache: 0.000017 seconds
Wir sehen einen deutlichen Unterschied in den Ausführungszeiten. Der Funktionsaufruf ohne Zwischenspeicherung dauert viel länger, insbesondere bei größeren Werten von n
. Während die zwischengespeicherten Versionen (beide @cache
Und @lru_cache
) werden viel schneller ausgeführt und haben vergleichbare Ausführungszeiten.
Einpacken
Mithilfe der @cache
Und @lru_cache
Dekoratoren können Sie die Ausführung von Funktionen, die teure Berechnungen oder rekursive Aufrufe beinhalten, erheblich beschleunigen. Den vollständigen Code finden Sie auf GitHub.
Wenn Sie nach einem umfassenden Leitfaden zu Finest Practices für die Verwendung von Python für Knowledge Science suchen, lesen Sie 5 Python Finest Practices für Knowledge Science.
Bala Priya C ist Entwicklerin und technische Redakteurin aus Indien. Sie arbeitet gerne an der Schnittstelle zwischen Mathematik, Programmierung, Datenwissenschaft und Inhaltserstellung. Ihre Interessens- und Fachgebiete umfassen DevOps, Datenwissenschaft und natürliche Sprachverarbeitung. Sie liest, schreibt, programmiert und trinkt gerne Kaffee! Derzeit arbeitet sie daran, ihr Wissen zu lernen und mit der Entwickler-Group zu teilen, indem sie Tutorials, Anleitungen, Meinungsbeiträge und mehr verfasst. Bala erstellt auch ansprechende Ressourcenübersichten und Programmier-Tutorials.