dictionary iterate - Livelli multipli di "collection.defaultdict" in Python





nested loop (5)


Guarda la risposta di nosklo here per una soluzione più generale.

class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

test:

a = AutoVivification()

a[1][2][3] = 4
a[1][3][3] = 5
a[1][2]['test'] = 6

print a

Produzione:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}}

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!




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




Uso:

d = defaultdict(lambda: defaultdict(int))

Questo creerà un nuovo defaultdict(int) ogni volta che si accede a una nuova chiave in d .




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



Non so se il seguente è pythonic o no, ma usa la funzione Python enumerate e stampa l'indice e il valore.

int_list = [8, 23, 45, 12, 78]
for index, value in enumerate(int_list):
   print(index, value)

Produzione:

0 8
1 23
2 45
3 12
4 78




python dictionary nested