Verschachteltes Standarddikt des Standarddikts

129

Gibt es eine Möglichkeit, ein Standarddikt auch zum Standard für das Standarddikt zu machen? (dh rekursives Standarddikt mit unendlicher Ebene?)

Ich möchte in der Lage sein:

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

Also kann ich tun x = defaultdict(defaultdict), aber das ist nur eine zweite Ebene:

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

Es gibt Rezepte, die dies tun können. Aber kann es einfach mit den normalen defaultdict-Argumenten gemacht werden?

Beachten Sie, dass hier gefragt wird, wie ein rekursives Standarddikt auf unendlicher Ebene erstellt werden soll. Es unterscheidet sich also von Python: Standarddikt des Standarddikts? , wie man ein zweistufiges Standarddikt macht.

Ich werde wahrscheinlich nur das Bündelmuster verwenden , aber als mir klar wurde, dass ich nicht wusste, wie ich das machen soll, hat mich das interessiert.

Corley Brigman
quelle
Mögliches Duplikat von Python: defaultdict of defaultdict?
Malioboro
2
Nicht wirklich ... der Frage wurden Informationen hinzugefügt, um anzugeben, warum. Das ist allerdings eine nützliche Frage.
Corley Brigman

Antworten:

167

Für eine beliebige Anzahl von Ebenen:

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": {}}}}}

Natürlich kann man das auch mit einem Lambda machen, aber ich finde Lambdas weniger lesbar. Auf jeden Fall würde es so aussehen:

rec_dd = lambda: defaultdict(rec_dd)
Andrew Clark
quelle
1
In der Tat ein perfektes Beispiel, danke. Könnten Sie es bitte auf den Fall erweitern, dass die Daten von json in defaultdict of defaultdict geladen werden?
David Belohrad
4
Eine Note. Wenn Sie versuchen, diesen Code beim Beizen zu verwenden lambda, funktioniert dies nicht.
Viacheslav Kondratiuk
166

Die anderen Antworten hier zeigen Ihnen, wie Sie eine erstellen, defaultdictdie "unendlich viele" enthält defaultdict, aber sie sprechen nicht das an, was meiner Meinung nach Ihr ursprüngliches Bedürfnis gewesen sein könnte, einfach ein Zwei-Tiefen-Standarddikt zu haben.

Sie haben vielleicht gesucht:

defaultdict(lambda: defaultdict(dict))

Die Gründe, warum Sie dieses Konstrukt bevorzugen könnten, sind:

  • Es ist expliziter als die rekursive Lösung und daher für den Leser wahrscheinlich verständlicher.
  • Dies ermöglicht, dass das "Blatt" des defaultdictetwas anderes als ein Wörterbuch ist, z. B.: defaultdict(lambda: defaultdict(list))Oderdefaultdict(lambda: defaultdict(set))
Chris W.
quelle
3
defaultdict (lambda: defaultdict (Liste)) Die richtige Form?
Yuvaraj Loganathan
Hoppla, ja, das lambdaFormular ist korrekt - da das defaultdict(something)ein wörterbuchähnliches Objekt zurückgibt, aber defaultdictein aufrufbares Objekt erwartet! Danke dir!
Chris W.
4
Dies wurde als mögliches Duplikat einer anderen Frage markiert ... aber es war nicht meine ursprüngliche Frage. Ich wusste, wie man ein zweistufiges Standarddiktat erstellt. Was ich nicht wusste, war, wie man es rekursiv macht. Diese Antwort ist in der Tat ähnlich zu stackoverflow.com/questions/5029934/…
Corley Brigman
Ein Nachteil des Lambda-Ansatzes ist, dass die von ihm erzeugten Objekte nicht eingelegt werden können ... aber Sie können dies dict(result)
umgehen,
53

Dafür gibt es einen raffinierten Trick:

tree = lambda: defaultdict(tree)

Dann können Sie Ihre xmit erstellen x = tree().

BrenBarn
quelle
22

Ähnlich wie die Lösung von BrenBarn, enthält jedoch nicht treezweimal den Namen der Variablen , sodass sie auch nach Änderungen am Variablenwörterbuch funktioniert:

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

Dann können Sie jedes neue xmit erstellen x = tree().


Für die defVersion können wir den Funktionsabschlussbereich verwenden, um die Datenstruktur vor dem Fehler zu schützen, bei dem vorhandene Instanzen nicht mehr funktionieren, wenn der treeName neu gebunden wird. Es sieht aus wie das:

from collections import defaultdict

def tree():
    def the_tree():
        return defaultdict(the_tree)
    return the_tree()
pts
quelle
4
Ich muss darüber nachdenken (es ist etwas komplexer). aber ich denke dein Punkt ist, dass wenn do x = tree (), aber dann kommt jemand später vorbei und tree = None, würde dieser immer noch funktionieren, und das würde nicht?
Corley Brigman
11

Ich würde auch eine Implementierung im OOP-Stil vorschlagen, die sowohl eine unendliche Verschachtelung als auch eine ordnungsgemäße Formatierung unterstützt repr.

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

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

Verwendung:

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': {}}
Stanislav Tsepa
quelle
1
Ordentlich ! Ich fügte hinzu , das Pass - Through von *argsund **kwargsdas es zu funktionieren wie das erlaubt defaultdict, nämlich eine dict mit Schlüsselwort - Argumenten zu erstellen. Dies ist nützlich für die Übergabe NestedDefaultDictanjson.load
Ciprian Tomoiagă
0

Hier ist eine rekursive Funktion zum Konvertieren eines rekursiven Standarddiktats in ein normales Diktat

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, {})
Dr. XD
quelle
0

Ich habe diese Antwort von Andrew hier begründet. Wenn Sie Daten von einem JSON oder einem vorhandenen Diktat in das Standarddikt des Nesters laden möchten, sehen Sie sich dieses Beispiel an:

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

Nucklehead
quelle