it-swarm.com.de

Gibt es einen Dekorateur, der einfach die Rückgabewerte der Funktion zwischenspeichert?

Folgendes berücksichtigen:

@property
def name(self):

    if not hasattr(self, '_name'):

        # expensive calculation
        self._name = 1 + 1

    return self._name

Ich bin neu, aber ich denke, die Zwischenspeicherung könnte in einen Dekorateur umgerechnet werden. Nur habe ich nicht so einen gefunden;)

PS: Die tatsächliche Berechnung hängt nicht von veränderlichen Werten ab

112
Tobias

Ab Python 3.2 gibt es einen eingebauten Dekorator:

@functools.lru_cache(maxsize=100, typed=False)

Decorator, um eine Funktion mit einer Memoizing-Funktion aufzurufen, die aufrufbar ist und die bis zu den letzten Aufrufe maximaler Größe speichert. Es kann Zeit gespart werden, wenn eine teure oder E/A-gebundene Funktion mit denselben Argumenten periodisch aufgerufen wird.

Beispiel eines LRU-Caches zum Berechnen von Fibonacci-Zahlen :

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> print([fib(n) for n in range(16)])
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> print(fib.cache_info())
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

Wenn Sie mit Python 2.x nicht weiterkommen, finden Sie hier eine Liste anderer kompatibler Memoization-Bibliotheken:

151
Paolo Moretti

Es klingt so, als würden Sie not nach einem Universal-Memo-Dekorator fragen (d. H., Sie sind nicht an dem allgemeinen Fall interessiert, in dem Sie Rückgabewerte für verschiedene Argumentwerte zwischenspeichern möchten). Das heißt, Sie möchten folgendes haben:

x = obj.name  # expensive
y = obj.name  # cheap

ein Universal-Memo-Dekorateur würde Ihnen folgendes geben:

x = obj.name()  # expensive
y = obj.name()  # cheap

Ich gehe davon aus, dass die Methodenaufruf-Syntax einen besseren Stil darstellt, da sie die Möglichkeit einer kostspieligen Berechnung nahelegt, während die Eigenschaftssyntax eine schnelle Suche vorschlägt.

[Update: Der klassenbasierte Memo-Dekorator, mit dem ich zuvor verlinkt hatte und hier zitiert wurde, funktioniert nicht für Methoden. Ich habe es durch eine Dekorationsfunktion ersetzt.] Wenn Sie einen Mehrzweck-Memo-Dekorator verwenden möchten, haben wir einen einfachen:

def memoize(function):
  memo = {}
  def wrapper(*args):
    if args in memo:
      return memo[args]
    else:
      rv = function(*args)
      memo[args] = rv
      return rv
  return wrapper

Verwendungsbeispiel:

@memoize
def fibonacci(n):
  if n < 2: return n
  return fibonacci(n - 1) + fibonacci(n - 2)

Ein anderer Memo-Dekorator mit einer Begrenzung der Cachegröße ist hier zu finden.

25
Nathan Kitchen
class memorize(dict):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args):
        return self[args]

    def __missing__(self, key):
        result = self[key] = self.func(*key)
        return result

Verwendungsbeispiel:

>>> @memorize
... def foo(a, b):
...     return a * b
>>> foo(2, 4)
8
>>> foo
{(2, 4): 8}
>>> foo('hi', 3)
'hihihi'
>>> foo
{(2, 4): 8, ('hi', 3): 'hihihi'}
20
acmerfight

Werkzeug hat einen cached_property Dekorateur ( docs , source )

9
Imran

Ich habe diese einfache Dekoratorklasse so codiert, dass sie Funktionsantworten zwischenspeichert. Ich finde es sehr nützlich für meine Projekte:

from datetime import datetime, timedelta 

class cached(object):
    def __init__(self, *args, **kwargs):
        self.cached_function_responses = {}
        self.default_max_age = kwargs.get("default_cache_max_age", timedelta(seconds=0))

    def __call__(self, func):
        def inner(*args, **kwargs):
            max_age = kwargs.get('max_age', self.default_max_age)
            if not max_age or func not in self.cached_function_responses or (datetime.now() - self.cached_function_responses[func]['fetch_time'] > max_age):
                if 'max_age' in kwargs: del kwargs['max_age']
                res = func(*args, **kwargs)
                self.cached_function_responses[func] = {'data': res, 'fetch_time': datetime.now()}
            return self.cached_function_responses[func]['data']
        return inner

Die Verwendung ist unkompliziert:

import time

@cached
def myfunc(a):
    print "in func"
    return (a, datetime.now())

@cached(default_max_age = timedelta(seconds=6))
def cacheable_test(a):
    print "in cacheable test: "
    return (a, datetime.now())


print cacheable_test(1,max_age=timedelta(seconds=5))
print cacheable_test(2,max_age=timedelta(seconds=5))
time.sleep(7)
print cacheable_test(3,max_age=timedelta(seconds=5))
6
Pablo Besada

DISCLAIMER: Ich bin der Autor von kids.cache .

Sie sollten kids.cache überprüfen, es enthält einen @cache-Dekorator, der für Python 2 und Python 3 funktioniert. Keine Abhängigkeiten, ca. 100 Codezeilen. Es ist sehr einfach zu verwenden, zum Beispiel, wenn Sie Ihren Code vor Augen haben, könnten Sie ihn folgendermaßen verwenden:

pip install kids.cache

Dann

from kids.cache import cache
...
class MyClass(object):
    ...
    @cache            # <-- That's all you need to do
    @property
    def name(self):
        return 1 + 1  # supposedly expensive calculation

Oder Sie können den @cache-Dekorator nach dem @property setzen (dasselbe Ergebnis).

Die Verwendung von Cache für eine Eigenschaft wird als Lazy Evaluation bezeichnet. kids.cache kann noch viel mehr (es funktioniert mit beliebigen Argumenten, Eigenschaften, beliebigen Methoden und sogar Klassen ...). Für erfahrene Benutzer unterstützt kids.cachecachetools, wodurch für Python 2 und Python 3 (LRU, LFU, TTL, RR-Cache) fantastische Cache-Speicher bereitgestellt werden.

WICHTIGER HINWEIS: Der Standard-Cache-Speicher von kids.cache ist ein Standard-Diktier. Dies ist nicht für lang laufende Programme mit immer anderen Abfragen zu empfehlen, da dies zu einem ständig wachsenden Cache-Speicher führen würde. Für diese Verwendung können Sie andere Cache-Speicher hinzufügen, z. B. (@cache(use=cachetools.LRUCache(maxsize=2)), um Ihre Funktion/Eigenschaft/Klasse/Methode zu dekorieren ...)

6
vaab

Ah, ich musste nur den richtigen Namen dafür finden: " Lazy property evaluation ".

Ich mache das auch viel; Vielleicht verwende ich dieses Rezept irgendwann in meinem Code.

5
Ken Arnold

Wenn Sie Django Framework verwenden, verfügt es über eine solche Eigenschaft, um eine Ansicht oder Antwort von APIs Mit @cache_page(time) zwischenzuspeichern, und es können auch andere Optionen vorhanden sein.

Beispiel:

@cache_page(60 * 15, cache="special_cache")
def my_view(request):
    ...

Weitere Details finden Sie hier .

3
Nikhil Kumar

Es gibt noch ein weiteres Beispiel für einen memoize -Dekorateur in Python Wiki:

http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize

Dieses Beispiel ist etwas intelligent, da die Ergebnisse nicht zwischengespeichert werden, wenn die Parameter veränderbar sind. (Überprüfen Sie den Code, es ist sehr einfach und interessant!)

3

Es gibt fastcache , was "C-Implementierung von Python 3 functools.lru_cache ist. Bietet eine Beschleunigung von 10-30x gegenüber der Standardbibliothek."

Gleiche gewählte Antwort , nur anderer Import:

from fastcache import lru_cache
@lru_cache(maxsize=128, typed=False)
def f(a, b):
    pass

Außerdem ist es in Anaconda installiert, im Gegensatz zu Funktionswerkzeugen, die installiert werden müssen .

2
Romi Kuntsman

Zusammen mit dem Memoize-Beispiel habe ich folgende Python-Pakete gefunden:

  • cachepy ; Es erlaubt, TTL und\oder die Anzahl der Aufrufe für zwischengespeicherte Funktionen einzurichten. Man kann auch einen verschlüsselten dateibasierten Cache verwenden ...
  • percache
2
bubble

@lru_cache ist mit Standardfunktionswerten nicht perfekt

mein mem Dekorateur:

import inspect


def get_default_args(f):
    signature = inspect.signature(f)
    return {
        k: v.default
        for k, v in signature.parameters.items()
        if v.default is not inspect.Parameter.empty
    }


def full_kwargs(f, kwargs):
    res = dict(get_default_args(f))
    res.update(kwargs)
    return res


def mem(func):
    cache = dict()

    def wrapper(*args, **kwargs):
        kwargs = full_kwargs(func, kwargs)
        key = list(args)
        key.extend(kwargs.values())
        key = hash(Tuple(key))
        if key in cache:
            return cache[key]
        else:
            res = func(*args, **kwargs)
            cache[key] = res
            return res
    return wrapper

und Code zum Testen:

from time import sleep


@mem
def count(a, *x, z=10):
    sleep(2)
    x = list(x)
    x.append(z)
    x.append(a)
    return sum(x)


def main():
    print(count(1,2,3,4,5))
    print(count(1,2,3,4,5))
    print(count(1,2,3,4,5, z=6))
    print(count(1,2,3,4,5, z=6))
    print(count(1))
    print(count(1, z=10))


if __== '__main__':
    main()

ergebnis - nur 3 mal mit Schlaf 

aber mit @lru_cache wird es 4 mal sein, weil dies:

print(count(1))
print(count(1, z=10))

wird doppelt berechnet (schlechtes Arbeiten mit Standardwerten)

1
Sublimer

Ich habe so etwas implementiert, dass Pickle für Beständigkeit und sha1 für kurze, sicherlich eindeutige IDs verwendet wurde. Grundsätzlich hat der Cache den Code der Funktion und die Argumentationsgeschichte gehasht, um ein sha1 zu erhalten, und dann nach einer Datei mit diesem sha1 im Namen gesucht. Wenn es vorhanden war, wurde es geöffnet und das Ergebnis zurückgegeben. Wenn nicht, ruft es die Funktion auf und speichert das Ergebnis (optional nur speichern, wenn die Verarbeitung etwas Zeit in Anspruch genommen hat).

Das heißt, ich würde schwören, dass ich ein existierendes Modul gefunden habe, das dies getan hat, und hier bin ich auf der Suche nach diesem Modul ... Das nächste, was ich finden kann, ist dieses, das in etwa richtig aussieht: http: // chase-seibert. github.io/blog/2011/11/23/pythondjango-disk-based-caching-decorator.html

Das einzige Problem, das ich dabei sehe, ist, dass es für große Eingaben nicht gut funktioniert, da es str (arg) hashes ist, was für große Arrays nicht einzigartig ist.

Es wäre schön, wenn ein unique_hash () - Protokoll vorhanden wäre, bei dem eine Klasse einen sicheren Hash des Inhalts zurückgeben würde. Ich habe das im Grunde manuell für die Typen implementiert, die mir wichtig waren.

1
Ben

Versuchen Sie es mit joblib http://pythonhosted.org/joblib/memory.html

from joblib import Memory
memory = Memory(cachedir=cachedir, verbose=0)
@memory.cache
    def f(x):
        print('Running f(%s)' % x)
        return x
0
Dror Hilman

Wenn Sie Django verwenden und Ansichten zwischenspeichern möchten, finden Sie weitere Informationen unter Antwort von Nikhil Kumar


Wenn Sie jedoch KEINE Funktionsergebnisse zwischenspeichern möchten, können Sie Django-cache-utils verwenden.

Django-Caches werden wiederverwendet und cached decorator verwendet:

from cache_utils.decorators import cached

@cached(60)
def foo(x, y=0):
    print 'foo is called'
    return x+y
0
Greg Dubicki

Python 3.8 cached_property decorator

https://docs.python.org/dev/library/functools.html#functools.cached_property

cached_property wurde erwähnt unter: https://stackoverflow.com/a/5295190/895245 aber es wird in 3.8 zusammengeführt, was großartig ist.

Dieser Dekorator kann als Caching von @property oder als Cleaner von @functools.lru_cache angesehen werden, wenn Sie keine Argumente haben.

Die Dokumente sagen:

@functools.cached_property(func)

Transformieren Sie eine Methode einer Klasse in eine Eigenschaft, deren Wert einmal berechnet und dann als normales Attribut für die Lebensdauer der Instanz zwischengespeichert wird. Ähnlich wie property (), mit dem Zusatz von Caching. Nützlich für teure berechnete Eigenschaften von Instanzen, die ansonsten effektiv unveränderlich sind.

Beispiel:

class DataSet:
    def __init__(self, sequence_of_numbers):
        self._data = sequence_of_numbers

    @cached_property
    def stdev(self):
        return statistics.stdev(self._data)

    @cached_property
    def variance(self):
        return statistics.variance(self._data)

Neu in Version 3.8.

Hinweis Für diesen Dekorator muss das Attribut dict für jede Instanz eine veränderbare Zuordnung sein. Dies bedeutet, dass dies bei einigen Typen, wie z. B. Metaklassen, nicht funktioniert (da die dict -Attribute in Typinstanzen schreibgeschützte Proxys für den Klassennamensraum sind) und bei solchen, die slots without angeben Einschließen von dict als einen der definierten Slots (da solche Klassen überhaupt kein dict -Attribut bereitstellen).