Speichern von Standard-Warwar in Python

So können Sie sich eine Python-Funktion merken:

def memo_square(a, cache={}): if a not in cache: cache[a] = a*a return cache[a] 

Der Empfang ist zu Unrecht wenig bekannt, daher werden wir unter dem Schnitt analysieren, wie er funktioniert und wofür er gedacht ist.

Erstens, wie und warum es funktioniert. memo_square (wie jede andere Funktion) ein Objekt der Funktionsklasse, für das unter anderem beim Erstellen des Objekts ein memo_square.__defaults__ -Tupel ausgefüllt ist. Erstens enthält es ein leeres Wörterbuch, wie im Funktionsheader angegeben:

 >>> memo_square.__defaults__ ({},) 

__defaults__ ist ein reguläres Tupel und Sie können seine Elemente nicht ändern. Richtig, Sie können den gesamten Satz von Standardwerten auf einmal ersetzen, jedoch nur durch ein anderes Tupel:

 >>> def test(a=1, b=2): ... print(a, b) ... >>> test.__defaults__ (1, 2) >>> test() 1 2 >>> test.__defaults__ = (', ', '') >>> test() ,  >>> test.__defaults__[1] = '' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment >>> test.__defaults__ = {0: ', ', 1: ''} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __defaults__ must be set to a tuple object 

Soryan, dieser Artikel kommt nicht nach Picaba. Okay, das ist nicht wichtig. Wichtig ist, dass mit Ausnahme des sehr gerissenen Codes func.__defaults__ einmal während der Programmzeit mit all seinen Elementen erstellt wird. Ein Tupel und seine Elemente werden nicht bei jedem Funktionsaufruf neu erstellt, sondern verwendet, solange die Funktion vorhanden ist. Aber zu ändern, wenn die Elemente selbst veränderlich sind, verbietet niemand sie. Die Unfähigkeit, mit solchen Elementen zu arbeiten, ist eine der häufigsten Möglichkeiten, sich in die Python zu schießen . Das Speichern von Werten zwischen Funktionsaufrufen kann jedoch sehr nützlich sein. Nach mehreren Aufrufen memo_square.__defaults__ aus:

 >>> memo_square(2) 4 >>> memo_square.__defaults__ ({2: 4},) >>> memo_square(5) 25 >>> memo_square.__defaults__ ({2: 4, 5: 25},) >>> memo_square(2) 4 >>> memo_square.__defaults__ ({2: 4, 5: 25},) 

Wenn die Funktion bereits für denselben Wert aufgerufen wurde, wird der Wert berechnet und der Cache entsprechend nicht aufgefüllt. Für ein Quadrat ist der Nutzen gering (genau genommen ist der Nutzen für ein Quadrat negativ, da das Suchen in einem Wörterbuch teurer ist als das Multiplizieren von zwei Zahlen), aber für wirklich teure Funktionen kann das Auswendiglernen / Zwischenspeichern nützlich sein. Natürlich können Sie es in Python auf mehrere Arten bereitstellen. Hier sind die Alternativen, die wir haben:

  • @ functools.lru_cache . Ein Dekorator aus dem functools-Modul, der sich an die letzten Funktionsaufrufe erinnert. Es ist zuverlässig und einfach, verwendet jedoch alle Parameter der Funktion als Schlüssel, was bedeutet, dass Hashfähigkeit erforderlich ist und nicht bemerkt werden kann, dass zwei formal unterschiedliche Parameterwerte äquivalent sind. Mit der ersten Anforderung ist alles klar, zum Beispiel über Funktionen aus Mengen, die Sie vergessen können. Nun, oder wenn Sie anrufen, konvertieren Sie sie in Frozenset. Was die zweite betrifft, habe ich zum Beispiel eine Funktion, die eine SQL-Verbindung und eine Nummer als Eingabe verwendet und die mit dieser Nummer verknüpften Daten manipuliert. Die Verbindung kann während des Programmbetriebs getrennt und wiederhergestellt werden, und der lru_cache-Cache fliegt dann ab. Aber er weiß, wie man nur eine begrenzte Anzahl von Anrufen zwischenspeichert (um Speicherlecks zu vermeiden) und ist gut dokumentiert.
  • Cache außerhalb der Funktion:

     def square(a): return a**a cache = {} for x in values: if x not in cache: cache[x] = x**x print cache[x] 

    Die Bedeutung ist dieselbe, aber viel umständlicher. Darüber hinaus ist die Cache-Variable außerhalb der Funktion sichtbar, obwohl sie nur zum Speichern verwendet wird. Auf den Cache während der Memoisierung mit dem Standardargument kann extern nur über func.__defaults__ , auf die versehentlich nur schwer func.__defaults__ werden kann.
  • Leeren Sie ein vollwertiges Objekt mit einem Cache und machen Sie seine Funktion zu einer Methode. Es ist in Bezug auf Architektur und Testbarkeit gut und ermöglicht es Ihnen, eine beliebig komplexe Caching-Logik beizubehalten, die jedoch aufgrund der im Objektcode enthaltenen Boilerplate noch umständlicher ist. Außerdem ist nicht klar, von was und was überhaupt zu erben ist, wenn es mehr als eine merkwürdige Funktion gibt.

Die Hauptsache, an der diese Methode des Auswendiglernen verliert, ist, dass sie nicht sehr idiomatisch ist. Als ich persönlich zum ersten Mal über diese Entscheidung stolperte, dachte ich ein paar Minuten darüber nach, was hier vor sich ging und warum. Andererseits begann ich in diesen paar Minuten etwas besser zu verstehen, wie die Python-Funktionen und ihre Argumente angeordnet sind. Selbst wenn Sie nicht die Standardargumente verwenden (zum Auswendiglernen oder zum Beispiel zur Beschleunigung der Namensauflösung ), ist es für jeden Ernährungsberater nützlich, diese Technik zu kennen.

Source: https://habr.com/ru/post/de426341/


All Articles