Zagnieżdżony defaultdict of defaultdict

Czy istnieje sposób, aby defaultdict był również domyślnym dla defaultdict? (tj. infinite-level recursive defaultdict?)

Chcę być w stanie zrobić:

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

Więc mogę zrobić x = defaultdict(defaultdict), ale to tylko drugi 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 normalnych argumentów defaultdict?

Zwróć uwagę, że jest to pytanie, Jak zrobić domyślną wersję na poziomie nieskończonym, więc jest ona różna od Python: defaultdict of defaultdict?, czyli jak zrobić dwupoziomowy defaultdict.

Prawdopodobnie skończę używając wzoru } bunch , ale kiedy zdałem sobie sprawę, że nie wiem, jak to zrobić, zainteresowało mnie to.

Author: smci, 2013-10-04

8 answers

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 to również zrobić z lambda, ale uważam, że lambda jest mniej czytelna. W każdym razie wyglądałoby to tak:

rec_dd = lambda: defaultdict(rec_dd)
 185
Author: Andrew Clark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-04 19:33:55

Inne odpowiedzi mówią ci, jak stworzyć defaultdict, która zawiera "nieskończenie wiele" defaultdict, ale nie rozwiązują tego, co myślę, że mogło być twoją początkową potrzebą, którą było po prostu posiadanie dwu-głębokiego domyślnego dict.

Być może szukałeś:

defaultdict(lambda: defaultdict(dict))

Powody, dla których możesz preferować ten konstrukt to:

  • jest bardziej jednoznaczne niż rozwiązanie rekurencyjne, a zatem prawdopodobnie bardziej zrozumiałe dla czytelnika.
  • umożliwia to "liść" defaultdict być czymś innym niż słownik, np.: defaultdict(lambda: defaultdict(list)) lub defaultdict(lambda: defaultdict(set))
 190
Author: Chris W.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-11 18:00:53

Istnieje sprytny trik, aby to zrobić:

tree = lambda: defaultdict(tree)

Następnie możesz utworzyć x za pomocą x = tree().

 57
Author: BrenBarn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-04 19:34:18

Podobne do rozwiązania Brenbarna, ale nie zawiera dwukrotnie nazwy zmiennej tree, więc działa nawet po zmianach w słowniku zmiennych:

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

Następnie możesz utworzyć każdy nowy x za pomocą x = tree().


Dla wersji def, możemy użyć funkcji closure scope, aby chronić strukturę danych przed wadą, w której istniejące instancje przestają działać, jeśli nazwa tree jest rebound. Wygląda to tak:

from collections import defaultdict

def tree():
    def the_tree():
        return defaultdict(the_tree)
    return the_tree()
 22
Author: pts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-14 20:28:21

Proponowałbym również implementację w stylu OOP, która obsługuje zarówno nieskończone zagnieżdżanie, jak i odpowiednio sformatowane repr.

class NestedDefaultDict(defaultdict):
    def __init__(self, *args, **kwargs):
        super(NestedDefaultDict, self).__init__(NestedDefaultDict, *args, **kwargs)

    def __repr__(self):
        return repr(dict(self))

Użycie:

my_dict = NestedDefaultDict()
my_dict['a']['b'] = 1
my_dict['a']['c']['d'] = 2
my_dict['b']

print(my_dict)  # {'a': {'b': 1, 'c': {'d': 2}}, 'b': {}}
 14
Author: Stanislav Tsepa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-06 13:46:13

Oparłem tę odpowiedź Andrew tutaj. Jeśli chcesz załadować dane z json lub istniejącego dict do domyślnego pakietu Nester, zobacz ten przykład:

def nested_defaultdict(existing=None, **kwargs):
    if existing is None:
        existing = {}
    if not isinstance(existing, dict):
        return existing
    existing = {key: nested_defaultdict(val) for key, val in existing.items()}
    return defaultdict(nested_defaultdict, existing, **kwargs)

Https://gist.github.com/nucklehead/2d29628bb49115f3c30e78c071207775

 1
Author: nucklehead,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-24 00:28:41

Oto funkcja rekurencyjna do konwersji rekurencyjnego domyślnego dict na normalny dict

def defdict_to_dict(defdict, finaldict):
    # pass in an empty dict for finaldict
    for k, v in defdict.items():
        if isinstance(v, defaultdict):
            # new level created and that is the new value
            finaldict[k] = defdict_to_dict(v, {})
        else:
            finaldict[k] = v
    return finaldict

defdict_to_dict(my_rec_default_dict, {})
 0
Author: Dr. XD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-10 03:15:11

Odpowiedź

@nucklehead może być rozszerzona również o obsługę tablic w JSON:

def nested_dict(existing=None, **kwargs):
    if existing is None:
        existing = defaultdict()
    if isinstance(existing, list):
        existing = [nested_dict(val) for val in existing]
    if not isinstance(existing, dict):
        return existing
    existing = {key: nested_dict(val) for key, val in existing.items()}
    return defaultdict(nested_dict, existing, **kwargs)
 0
Author: Josh Olson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-15 03:52:52