Ein kleiner Ärger mit dict.setdefault
ist, dass es immer sein zweites Argument auswertet (wenn natürlich gegeben), auch wenn das erste das erste Argument bereits ein Schlüssel im Wörterbuch ist.Wie implementiert man einen Lazy-Setdefault?
Zum Beispiel:
import random
def noisy_default():
ret = random.randint(0, 10000000)
print 'noisy_default: returning %d' % ret
return ret
d = dict()
print d.setdefault(1, noisy_default())
print d.setdefault(1, noisy_default())
Dies erzeugt ouptut wie folgt aus:
noisy_default: returning 4063267
4063267
noisy_default: returning 628989
4063267
Da die letzte Zeile bestätigt, die zweite Ausführung von noisy_default
unnötig ist, von diesem Punkt, da der Schlüssel 1
ist bereits vorhanden in d
(mit Wert 4063267
).
Ist es möglich, eine Unterklasse von dict
zu implementieren, deren setdefault
Methode das zweite Argument faul auswertet?
EDIT:
Im Folgenden ist eine Implementierung inspiriert von BrenBarn Kommentar und Antwort Pavel Anossov. Während ich dabei war, habe ich eine faule Version von get implementiert, da die zugrunde liegende Idee im Wesentlichen die gleiche ist. Jetzt
class LazyDict(dict):
def get(self, key, thunk=None):
return (self[key] if key in self else
thunk() if callable(thunk) else
thunk)
def setdefault(self, key, thunk=None):
return (self[key] if key in self else
dict.setdefault(self, key,
thunk() if callable(thunk) else
thunk))
, das Snippet
d = LazyDict()
print d.setdefault(1, noisy_default)
print d.setdefault(1, noisy_default)
erzeugt eine Ausgabe wie folgt aus:
noisy_default: returning 5025427
5025427
5025427
Beachten Sie, dass das zweite Argument zu d.setdefault
oben ist jetzt eine aufrufbare, kein Funktionsaufruf. Wenn das zweite Argument zu LazyDict.get
oder LazyDict.setdefault
nicht aufrufbar ist, verhalten sie sich genauso wie die entsprechenden dict
Methoden.
Wenn man einen aufrufbaren als Standardwert passieren will sich (das heißt, nicht gemeint genannt werden), oder, wenn das aufrufbare aufgerufen werden erfordert Argumente, prepend lambda:
an die entsprechende Argument. ZB:
class LazyButHonestDict(dict):
def lazyget(self, key, thunk=lambda: None):
return self[key] if key in self else thunk()
def lazysetdefault(self, key, thunk=lambda: None):
return (self[key] if key in self else
self.setdefault(key, thunk()))
Sie können nicht das zweite Argument nicht bewerten. Sie müssten dieses Argument in eine Funktion (z. B. mit "Lambda") einbinden und dann mit "setdefault" die Funktion nur bei Bedarf aufrufen. – BrenBarn
Kann ich vorschlagen, dass Sie '* args, ** kwargs' zu den Signaturen von' lazetget', 'lazysetdefault' und dem Aufruf von' thunk() 'hinzufügen? Dies würde Ihren faulen Sachen erlauben, Parameter zu nehmen. z.B. 'lbd.lazysetdefault ('total', sum, [1, 2, 3, 4], start = 2)' – Hounshell