2013-10-04 9 views
58

Czy istnieje sposób, aby defaultdict również był domyślny dla defaultdict? IOW, jeśli zrobić:defaultdict defaultdict, zagnieżdżony

x = defaultdict(...stuff...) 
x[0][1][0] 
{} 

To, co chcę. Prawdopodobnie skończy się używanie wzoru grona, ale kiedy zrozumiałem, że nie wiem, jak to zrobić, zainteresowało mnie to.

Tak, mogę to zrobić:

x = defaultdict(defaultdict) 

ale to tylko jeden poziom:

x[0] 
{} 
x[0][0] 
KeyError: 0 

Istnieją przepisy, które mogą to zrobić. Ale czy można to zrobić po prostu używając zwykłych argumentów defaultdict?

Należy pamiętać, że ktoś oznaczył to jako duplikat Python: defaultdict of defaultdict?, ale to nie jest to samo pytanie ... pytanie brzmiało, jak wykonać dwupoziomowy defaultdict; ten jest jak wykonać rekursywny defaultdict na poziomie nieskończonym.

+0

Możliwy duplikat [Python: defaultdict z defaultdict] (http://stackoverflow.com/questions/5029934/python-defaultdict-of-defaultdict) – malioboro

+0

Niezupełnie ... dodano informację do pytania, aby wskazać dlaczego. Chociaż jest to przydatne pytanie. –

Odpowiedz

88

dla dowolnej liczby poziomów:

def rec_dd(): 
    return defaultdict(rec_dd) 

>>> x = rec_dd() 
>>> x['a']['b']['c']['d'] 
defaultdict(<function rec_dd at 0x7f0dcef81500>, {}) 
>>> print json.dumps(x) 
{"a": {"b": {"c": {"d": {}}}}} 

Oczywiście można też zrobić to z lambda, ale uważam lambdy być mniej czytelny. W każdym przypadku będzie to wyglądać tak:

rec_dd = lambda: defaultdict(rec_dd) 
+1

Rzeczywiście doskonały przykład, dzięki. Czy mógłbyś rozszerzyć to na przypadek, że dane są ładowane z json do defaultdict z defaultdict? –

+0

Jedna uwaga. Jeśli próbujesz użyć tego kodu podczas marszczenia 'lambda' nie zadziała. –

25

Jest sprytny trik za to, że:

tree = lambda: defaultdict(tree) 

Następnie można stworzyć swój x z x = tree().

17

podobne do rozwiązania BrenBarn, ale nie zawiera nazwę zmiennej tree dwa razy, więc to działa nawet po zmianach zmiennej słownika:

tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a)))) 

Następnie można utworzyć każdy nowy x z x = tree().


Dla wersji def, możemy użyć funkcji zakres zamknięcia chronić strukturę danych ze skazą, gdzie istniejące instancje przestać działać jeśli nazwa tree jest odbicie. Wygląda to tak:

from collections import defaultdict 

def tree(): 
    def the_tree(): 
     return defaultdict(the_tree) 
    return the_tree() 
+4

Będę musiał pomyśleć o tym (jest trochę bardziej skomplikowany). ale myślę, że twoja uwaga jest taka, że ​​jeśli zrobisz x = tree(), ale potem ktoś przyjdzie później i zrobi drzewo = None, to nadal by działało, a to by nie było? –

+3

Prawidłowo, o to mi chodzi. – pts

75

innych odpowiedzi tutaj powiedzieć, jak stworzyć defaultdict który zawiera „nieskończenie wiele” defaultdict, ale nie rozwiązują one co myślę mogło początkowej potrzebę, który był po prostu mieć dwustopniowy defaultdict.

Możesz szukaliśmy:

defaultdict(lambda: defaultdict(dict)) 

Powody dlaczego może wolisz ten konstrukt są:

  • To jest bardziej wyraźny niż rekurencyjne rozwiązania, a więc prawdopodobnie bardziej zrozumiałe dla czytelnik.
  • Umożliwia to "liść" z defaultdict być coś innego niż słowniku, np ,: defaultdict(lambda: defaultdict(list)) lub defaultdict(lambda: defaultdict(set))
+2

defaultdict (lambda: defaultdict (list)) Prawidłowy formularz? –

+0

Ooops, tak, forma 'lambda' jest poprawna - ponieważ' defaultdict (something) 'zwraca obiekt podobny do słownika, ale' defaultdict' oczekuje wywołania! Dziękuję Ci! –

+0

Zostało to oznaczone jako możliwy duplikat innego pytania ... ale nie było to moje pierwotne pytanie. Wiedziałem, jak utworzyć dwupoziomowy defaultdict; nie wiedziałem, jak to uczynić rekursywnym. Ta odpowiedź jest podobna do http://stackoverflow.com/questions/5029934/python-defaultdict-of-defaultdict –

Powiązane problemy