Livelli multipli di "collection.defaultdict" in Python



Answers

Un altro modo per creare un defaultdict selezionabile e nidificato consiste nell'utilizzare un oggetto parziale anziché un lambda:

from functools import partial
...
d = defaultdict(partial(defaultdict, int))

Questo funzionerà perché la classe defaultdict è accessibile a livello globale a livello di modulo:

"Non puoi decapitare un oggetto parziale a meno che la funzione [o in questo caso, la classe] che esegue il wrapping sia globalmente accessibile ... sotto il suo __name__ (all'interno del suo __modulo__)" - Il Pickling ha avvolto le funzioni parziali

Question

Grazie ad alcune persone fantastiche su SO, ho scoperto le possibilità offerte da collections.defaultdict , in particolare in termini di leggibilità e velocità. Li ho usati per il successo.

Ora vorrei implementare tre livelli di dizionari, i due principali sono defaultdict e il più basso int . Non trovo il modo appropriato per farlo. Ecco il mio tentativo:

from collections import defaultdict
d = defaultdict(defaultdict)
a = [("key1", {"a1":22, "a2":33}),
     ("key2", {"a1":32, "a2":55}),
     ("key3", {"a1":43, "a2":44})]
for i in a:
    d[i[0]] = i[1]

Ora questo funziona, ma il seguente, che è il comportamento desiderato, non:

d["key4"]["a1"] + 1

Sospetto che avrei dovuto dichiarare da qualche parte che il secondo livello defaultdict è di tipo int , ma non ho trovato dove o come farlo.

La ragione per cui sto usando defaultdict in primo luogo è di evitare di dover inizializzare il dizionario per ogni nuova chiave.

Qualche suggerimento più elegante?

Grazie a Pythoneers!




Come per la richiesta di @ rschwieb per D['key'] += 1 , possiamo espandere su previous sostituendo l'aggiunta definendo il metodo __add__ , per fare in modo che si comportino più come un collections.Counter()

Prima __missing__ sarà chiamato per creare un nuovo valore vuoto, che sarà passato in __add__ . Testiamo il valore, contando su valori vuoti per essere False .

Vedi emulare tipi numerici per ulteriori informazioni sulla sostituzione.

from numbers import Number


class autovivify(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

    def __add__(self, x):
        """ override addition for numeric types when self is empty """
        if not self and isinstance(x, Number):
            return x
        raise ValueError

    def __sub__(self, x):
        if not self and isinstance(x, Number):
            return -1 * x
        raise ValueError

Esempi:

>>> import autovivify
>>> a = autovivify.autovivify()
>>> a
{}
>>> a[2]
{}
>>> a
{2: {}}
>>> a[4] += 1
>>> a[5][3][2] -= 1
>>> a
{2: {}, 4: 1, 5: {3: {2: -1}}}

Piuttosto che controllare l'argomento è un numero (molto non-python, amirite!) Potremmo semplicemente fornire un valore 0 predefinito e quindi tentare l'operazione:

class av2(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

    def __add__(self, x):
        """ override addition when self is empty """
        if not self:
            return 0 + x
        raise ValueError

    def __sub__(self, x):
        """ override subtraction when self is empty """
        if not self:
            return 0 - x
        raise ValueError



Links