Una piccola seccatura con dict.setdefault
è che valuta sempre il suo secondo argomento (quando viene dato, ovviamente), anche quando il primo il primo argomento è già una chiave nel dizionario.Come implementare un setdefault pigro?
Ad esempio:
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())
Questo produce ouptut come la seguente:
noisy_default: returning 4063267
4063267
noisy_default: returning 628989
4063267
Come ultima linea conferma, la seconda esecuzione di noisy_default
è necessaria perché a questo punto il tasto 1
è già presente in d
(con valore 4063267
).
È possibile implementare una sottoclasse di dict
il cui metodo setdefault
valuta pigramente il secondo argomento?
EDIT:
Di seguito è un'implementazione ispirato dal commento di BrenBarn e la risposta di Pavel Anossov. Mentre lo facevo, sono andato avanti e ho implementato anche una versione lenta di get, dato che l'idea di base è essenzialmente la stessa.
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))
momento, frammento
d = LazyDict()
print d.setdefault(1, noisy_default)
print d.setdefault(1, noisy_default)
produce output del tipo:
noisy_default: returning 5025427
5025427
5025427
noti che il secondo parametro di d.setdefault
sopra è ora un richiamabile, non una chiamata di funzione.
Quando il secondo argomento su LazyDict.get
o LazyDict.setdefault
non è un chiamabile, si comportano allo stesso modo dei corrispondenti metodi dict
.
Se si vuole passare un richiamabile come valore predefinito stessa (cioè, non destinata ad essere chiamato), o se il richiamabile essere chiamato richiede argomenti, anteporre lambda:
all'argomento appropriato. Ad esempio:
d1.setdefault('div', lambda: div_callback)
d2.setdefault('foo', lambda: bar('frobozz'))
Coloro che non amano l'idea di override get
e setdefault
, e/o la conseguente necessità di testare per l'esigibilità, ecc, può usare questa versione invece:
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()))
Non è possibile farlo non valutare il secondo argomento. Quello che dovresti fare è racchiudere quell'argomento in una funzione (ad es. Con 'lambda') e poi chiamare' setdefault' la funzione solo se necessario. – BrenBarn
Posso suggerire di aggiungere '* args, ** kwargs' alle firme di' lazyget', 'lazysetdefault' e la chiamata a' thunk() '? Ciò consentirebbe ai tuoi oggetti pigri di prendere parametri. per esempio. 'lbd.lazysetdefault ('total', sum, [1, 2, 3, 4], start = 2)' – Hounshell