222 votes

Nested defaultdict de defaultdict

Existe-t-il un moyen de faire en sorte qu'un defaultdict soit également le défaut pour le defaultdict ? (c'est-à-dire un defaultdict récursif à niveau infini?)

Je veux être capable de faire :

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

Donc, je peux faire x = defaultdict(defaultdict), mais ce n'est que pour un deuxième niveau :

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

Il existe des recettes pour faire cela. Mais est-il possible de le faire simplement en utilisant les arguments normaux de defaultdict ?

Notez que cette question porte sur la façon de créer un defaultdict récursif à niveau infini, donc elle est distincte de Python: defaultdict of defaultdict?, qui traitait de la façon de créer un defaultdict à deux niveaux.

Je finirai probablement par utiliser le motif bunch, mais lorsque j'ai réalisé que je ne savais pas faire cela, cela m'a intéressé.

1voto

nucklehead Points 96

J'ai basé cela sur la réponse d'Andrew ici. Si vous cherchez à charger des données à partir d'un json ou d'un dictionnaire existant dans le nester defaultdict, consultez cet exemple:

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

1voto

BML Points 103

Voici une fonction pour un defaultdict de base arbitraire pour une profondeur de nesting arbitraire.

(cross posting from Can't pickle defaultdict)

def wrap_defaultdict(instance, times=1):
    """Envelopper une instance un nombre arbitraire de `times` pour créer un defaultdict imbriqué.

    Paramètres
    ----------
    instance - liste, dictionnaire, entier, collections.Counter
    times - le nombre de clés imbriquées au-dessus de `instance`; si `times=3` dd[un][deux][trois] = instance

    Remarques
    -----
    l'utilisation de `x.copy` permet le pickling (chargement vers un cluster ipyparallel ou pkldump)
        - merci https://stackoverflow.com/questions/16439301/cant-pickle-defaultdict
    """
    from collections import defaultdict

    def _dd(x):
        return defaultdict(x.copy)

    dd = defaultdict(instance)
    for i in range(times-1):
        dd = _dd(dd)

    return dd

0voto

Dr. XD Points 21

Voici une fonction récursive pour convertir un dictionnaire par défaut récursif en un dictionnaire normal

def defdict_to_dict(defdict, finaldict):
    # passez un dictionnaire vide pour finaldict
    for k, v in defdict.items():
        if isinstance(v, defaultdict):
            # un nouveau niveau est créé et c'est la nouvelle valeur
            finaldict[k] = defdict_to_dict(v, {})
        else:
            finaldict[k] = v
    return finaldict

defdict_to_dict(mon_rec_default_dict, {})

0voto

Josh Olson Points 182

La réponse de @nucklehead peut également être étendue pour gérer les tableaux en 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)

0voto

hi2meuk Points 186

Basé sur la réponse de Chris W, cependant, pour résoudre le problème de l'annotation de type, vous pourriez en faire une fonction factory qui définit les types détaillés. Par exemple, voici la solution finale à mon problème lorsque je recherchais cette question :

def frequency_map_factory() -> dict[str, dict[str, int]]:
    """
    Fournit un enregistreur de: par X:str, fréquence des occurrences de Y:str.
    """
    return defaultdict(lambda: defaultdict(int))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X