2013-05-05 11 views
6

Il mio codice attualmente deve contare le cose in un dict fortemente nidificato in un altro. Ho elementi che devono essere indicizzati da 3 valori e quindi contati. Così, prima che il mio ciclo, ho inizializzare una nidificato defaultdict in questo modo:Altro modo Pythonic per contare le cose in un predefinito predefinito pesantemente nidificato

from collections import defaultdict 

type_to_count_dic = defaultdict(
     lambda: defaultdict(
      lambda: defaultdict(int) 
     ) 
    ) 

Il che mi permette di contare gli elementi all'interno di un loop stretto in questo modo:

for a in ...: 
    for b in ...: 
     for c in ...: 
      type_to_count_dic[a][b][c] += 1 

Mi sento come l'inizializzazione di tutte quelle defaultdict s sembra molto come fare una dichiarazione di tipo in qualcosa come Java. C'è un modo più idiomatico/pitonico di fare qualcosa del genere?

risposta

8
from collections import defaultdict 

class _defaultdict(defaultdict): 
    def __add__(self, other): 
     return other 

def CountTree(): 
    return _defaultdict(CountTree) 

>>> t = CountTree() 
>>> t['a'] 
defaultdict(<function CountTree at 0x9e5c3ac>, {}) 
>>> t['a']['b']['c'] += 1 
>>> print t['a']['b']['c'] 
1 
3

Dal momento che si contano le cose, è necessario utilizzare un contatore per l'interno-più dict:

import collections 
defaultdict = collections.defaultdict 
Counter = collections.Counter 

x = defaultdict(lambda: defaultdict(Counter)) 

for a in A: 
    for b in B: 
     x[a][b].update(C) 

utilizzando un contatore vi darà accesso a metodi utili come most_common.

A seconda di ciò che si intende fare con questa dict, potrebbe non essere necessario l'annidamento profondo. Invece, potresti usare una tupla per la chiave. Ad esempio,

import collections 
import itertools as IT 

A = range(2) 
B = 'XYZ' 
C = 'abc' 
x = collections.Counter(IT.product(A, B, C)) 
print(x) 

cede

A = range(2) 
B = 'XYZ' 
C = 'abc' 
x = collections.Counter(IT.product(A, B, C)) 
print(x) 

cede

Counter({(0, 'X', 'c'): 1, (0, 'Z', 'a'): 1, (1, 'Z', 'a'): 1, (1, 'X', 'c'): 1, (1, 'Z', 'b'): 1, (0, 'X', 'b'): 1, (0, 'Y', 'a'): 1, (1, 'Y', 'a'): 1, (0, 'Z', 'c'): 1, (1, 'Z', 'c'): 1, (0, 'X', 'a'): 1, (0, 'Y', 'b'): 1, (1, 'X', 'a'): 1, (1, 'Y', 'b'): 1, (0, 'Z', 'b'): 1, (1, 'Y', 'c'): 1, (1, 'X', 'b'): 1, (0, 'Y', 'c'): 1}) 
2

Sto assumendo si sta solo aggiungendo ad ogni contatore quando certo con le dizioni sono soddisfatte, o eventualmente aggiungendo valori diversi a seconda delle condizioni? Altrimenti sicuramente il valore di ogni contatore sarà sempre 1?

Detto questo, la soluzione più semplice a cui riesco a pensare è quella di creare un singolo dettato su una tupla dei tre valori del loop. Ad esempio qualcosa del genere:

dict(((a,b,c),1) for a in A for b in B for c in C) 

Ma come ho detto, questo ti darà solo 1 in ogni contatore. Avrai bisogno di sostituire il nella espressione di cui sopra con una certa condizione o chiamata di funzione che restituisce qualcosa di più appropriato a seconda dei valori di un, b e c.

0

ho avuto una simile esigenza, e ha creato il seguente:

import json 

from collections import defaultdict 


class NestedDefaultDict(defaultdict): 
    def __init__(self, depth, default=int, _root=True): 
     self.root = _root 
     self.depth = depth 
     if depth > 1: 
      cur_default = lambda: NestedDefaultDict(depth - 1, 
                default, 
                False) 
     else: 
      cur_default = default 
     defaultdict.__init__(self, cur_default) 

    def __repr__(self): 
     if self.root: 
      return "NestedDefaultDict(%d): {%s}" % (self.depth, 
                defaultdict.__repr__(self)) 
     else: 
      return defaultdict.__repr__(self) 


# Quick Example 
core_data_type = lambda: [0] * 10 
test = NestedDefaultDict(3, core_data_type) 
test['hello']['world']['example'][5] += 100 
print test 
print json.dumps(test) 

# Code without custom class. 
test = defaultdict(lambda: defaultdict(lambda: defaultdict(core_data_type))) 
test['hello']['world']['example'][5] += 100 
print test 
print json.dumps(test) 

Se io alla fine aggiornarlo Ho anche creato un gist: https://gist.github.com/KyleJamesWalker/8573350

Problemi correlati