2010-05-26 10 views
57

Klasa ma konstruktora, który przyjmuje jeden parametr:Czy istnieje sprytny sposób przekazania klucza do default_factory default_factory?

class C(object): 
    def __init__(self, v): 
     self.v = v 
     ... 

gdzieś w kodzie, to jest przydatna dla wartości w dict znać swoje klucze.
chcę użyć defaultdict z kluczem przekazany do nowonarodzonych wartości domyślnych:

d = defaultdict(lambda : C(here_i_wish_the_key_to_be)) 

jakieś sugestie?

+0

więc jaki masz problem? – SilentGhost

+3

'd = defaultdict (klucz lambda: C (klucz))'? –

+0

@jellybean: jak by to sprawiło? – SilentGhost

Odpowiedz

87

Trudno zakwalifikować jako mądry - ale subclassing jest twoim przyjacielem:

class keydefaultdict(defaultdict): 
    def __missing__(self, key): 
     if self.default_factory is None: 
      raise KeyError(key) 
     else: 
      ret = self[key] = self.default_factory(key) 
      return ret 

d = keydefaultdict(C) 
d[x] # returns C(x) 
+8

To jest właśnie brzydota, której próbuję uniknąć ... Nawet używanie prostego dyktatu i sprawdzanie istnienia klucza jest znacznie czystsze. –

+0

@Paul: a jednak to jest twoja odpowiedź. Brzydota? Daj spokój! – tzot

+4

Myślę, że zamierzam wziąć ten fragment kodu i umieścić go w moim spersonalizowanym ogólnym module narzędziowym, aby móc go używać, kiedy tylko chcę. Nie tak brzydki w ten sposób ... – weronika

7

Nie sądzę, trzeba defaultdict tutaj w ogóle. Dlaczego nie skorzystać z metody dict.setdefault?

>>> d = {} 
>>> d.setdefault('p', C('p')).v 
'p' 

To oczywiście stworzy wiele instancji C. W przypadku, jest to problem, myślę, że prostsze podejście zrobi:

>>> d = {} 
>>> if 'e' not in d: d['e'] = C('e') 

Byłoby szybciej niż defaultdict lub innej alternatywy, jak dalece można widzę.

ETA dotyczące prędkości in testu vs. użyciem try-except:

>>> def g(): 
    d = {} 
    if 'a' in d: 
     return d['a'] 


>>> timeit.timeit(g) 
0.19638929363557622 
>>> def f(): 
    d = {} 
    try: 
     return d['a'] 
    except KeyError: 
     return 


>>> timeit.timeit(f) 
0.6167065411074759 
>>> def k(): 
    d = {'a': 2} 
    if 'a' in d: 
     return d['a'] 


>>> timeit.timeit(k) 
0.30074866358404506 
>>> def p(): 
    d = {'a': 2} 
    try: 
     return d['a'] 
    except KeyError: 
     return 


>>> timeit.timeit(p) 
0.28588609450770264 
+2

Jest to wysoce nieekonomiczne w przypadkach, w których d jest dostępny wiele razy, i tylko rzadko brakuje klucz: C (klucz) będzie więc tworzyć mnóstwo niepotrzebnych obiektów do gromadzenia GC. Ponadto w moim przypadku występuje dodatkowy ból, ponieważ tworzenie nowych obiektów C jest powolne. –

+0

@Paul: Właśnie tak. Proponuję wtedy jeszcze prostszą metodę, zobacz moją edycję. – SilentGhost

+0

Nie jestem pewien, czy jest szybszy niż defaultdict, ale tak właśnie robię (zobacz mój komentarz do odpowiedzi THC4k). Miałem nadzieję, że istnieje prosty sposób na zhackowanie się z faktem, że default_factory nie przyjmuje argumentów, aby kod był nieco bardziej elegancki. –

12

Nie, nie ma.

Implementacja defaultdict nie może być skonfigurowana do przekazywania brakującego key do default_factory bez opakowania. Jedyną opcją jest zaimplementowanie własnej podklasy defaultdict, zgodnie z sugestią @JochenRitzel powyżej.

Ale to nie jest "sprytne" lub prawie tak czyste, jak standardowe rozwiązanie biblioteki (jeśli istniało). Zatem odpowiedź na twoje lapidarne, tak/nie pytanie jest wyraźnie "nie".

Szkoda, że ​​w standardowej bibliotece brakuje tak często potrzebnego narzędzia.

Powiązane problemy