Jedną z mniejszych irytacji z dict.setdefault
jest to, że zawsze ocenia swój drugi argument (oczywiście podany), nawet jeśli pierwszy pierwszy argument jest już kluczem w słowniku.Jak zaimplementować leniwy setdefault?
Na przykład:
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())
ta produkuje ouptut jak następuje:
noisy_default: returning 4063267
4063267
noisy_default: returning 628989
4063267
Jako ostatnia linia potwierdza, drugie wykonanie noisy_default
jest niepotrzebny, ponieważ od tego momentu kluczem 1
jest już obecne w d
(o wartości 4063267
).
Czy jest możliwe zaimplementowanie podklasy dict
, której metoda setdefault
ocenia leniwie swój drugi argument?
EDIT:
Poniżej jest implementacja zainspirowany komentarzem BrenBarn i odpowiedzi Pavel Anossov użytkownika. W tym czasie zastosowałem leniwą wersję geta, ponieważ idea jest zasadniczo taka sama.
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))
Teraz fragment
d = LazyDict()
print d.setdefault(1, noisy_default)
print d.setdefault(1, noisy_default)
produkuje wyjście tak:
noisy_default: returning 5025427
5025427
5025427
Zauważ, że drugi argument d.setdefault
powyżej obecnie jest wymagalne, a nie wywołanie funkcji.
Gdy drugi argument do LazyDict.get
lub LazyDict.setdefault
nie jest wywoływalny, zachowują się one tak samo, jak odpowiadające im metody dict
.
Jeśli ktoś chce przejść wywoływalnym jako wartość domyślną samego (tj nie rozumie się nazywa), lub jeżeli wpłacone na miano wymaga argumentów, prepend lambda:
do odpowiedniej argumentacji. Np:
d1.setdefault('div', lambda: div_callback)
d2.setdefault('foo', lambda: bar('frobozz'))
Ci, którzy nie lubią ideę nadrzędnymi get
i setdefault
i/lub powstałego konieczności badania dla callability, itd., Mogą korzystać z tej wersji Zamiast:
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()))
Nie można zrobić, aby nie oceniać drugiego argumentu. To, co musisz zrobić, to zawrzeć ten argument w funkcji (np. Z 'lambda'), a następnie ustawić' setdefault' wywoływanie funkcji tylko w razie potrzeby. – BrenBarn
Czy mogę zaproponować dodanie '* args, ** kwargs' do sygnatur' lazyget', 'lazysetdefault' i wywołanie' thunk() '? To pozwoli twoim leniwym rzeczom na przyjmowanie parametrów. na przykład 'lbd.lazysetdefault ('total', sum, [1, 2, 3, 4], start = 2)' – Hounshell