2013-08-29 10 views
11

Byłoby wygodne jeśli defaultdict można zainicjować wzdłuż następujących liniidefaultdict jednoetapowa inicjalizacji

d = defaultdict(list, (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), 
    ('b', 3))) 

produkować

defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}) 

Zamiast dostać

defaultdict(<type 'list'>, {'a': 2, 'c': 3, 'b': 3, 'd': 4}) 

Aby dostań to, czego potrzebuję, kończę, że muszę to zrobić:

To jest IMO o jeden krok więcej niż powinno być, czy tu czegoś brakuje?

+0

Jak korzystać z dołączania na liście? A co z innymi typami? –

+0

@Jonclements, dobry punkt. Jednak można by pomyśleć, że "lista" jest przypadkiem użycia dość powszechnym, że metoda wygody (być może metoda klasy) jest uzasadniona? – iruvar

+3

Czy ta metoda wygody nie byłaby dokładnie tym, co właśnie napisałeś na końcu postu? Dlaczego nie zawinąć tych trzech linii w funkcję i nazwać to dniem? –

Odpowiedz

9

Zachowanie, które opisujesz, nie będzie zgodne z innymi zachowaniami.Wygląda na to, co chcesz, jest taka, że ​​FooDict

>>> f = FooDict() 
>>> f['a'] = 1 
>>> f['a'] = 2 
>>> f['a'] 
[1, 2] 

Możemy to zrobić, ale nie z defaultdict; Nazwijmy to AppendDict

import collections 

class AppendDict(collections.MutableMapping): 
    def __init__(self, container=list, append=None, pairs=()): 
     self.container = collections.defaultdict(container) 
     self.append = append or list.append 
     for key, value in pairs: 
      self[key] = value 

    def __setitem__(self, key, value): 
     self.append(self.container[key], value) 

    def __getitem__(self, key): return self.container[key] 
    def __delitem__(self, key): del self.container[key] 
    def __iter__(self): return iter(self.container) 
    def __len__(self): return len(self.container) 
3

Sortowanie i itertools.groupby przejść długą drogę:

>>> L = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)] 
>>> L.sort(key=lambda t:t[0]) 
>>> d = defaultdict(list, [(tup[0], [t[1] for t in tup[1]]) for tup in itertools.groupby(L, key=lambda t: t[0])]) 
>>> d 
defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}) 

Aby to bardziej z jedną wkładką:

L = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)] 
d = defaultdict(list, [(tup[0], [t[1] for t in tup[1]]) for tup in itertools.groupby(sorted(L, key=operator.itemgetter(0)), key=lambda t: t[0])]) 

Hope this helps

+4

Jeśli OP nie lubi perfekcyjnej pętli 'for', wątpię, że' itertools.groupby', 'sort', kompilacja list, a także' lambda' lub 'itemgetter' będzie odwoływać .. – DSM

+0

Ciekawy. Zauważ jednak, że "sort" i "groupby" kończą się tutaj, wykonując całą pracę nóg. abyś mógł równie łatwo przekazać dane wyjściowe z 'groupby' do zwykłego' dict' zamiast 'defaultdict'! – iruvar

+0

@ 1_CR: Masz rację. Ale, dałem ci defaultdict, ponieważ poprosiłeś o jeden. – inspectorG4dget

13

Co ty widocznie brakuje że defaultdict jest prostą (nie szczególnie "magiczną") podklasą dict. Cały pierwszy argument to funkcja fabryczna dla z brakującymi kluczami. Po zainicjowaniu modelu defaultdict następuje inicjowanie dict.

Jeśli chcesz produkować

defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}) 

powinno być inicjowanie to tak byś zainicjować jakiekolwiek inne dict których wartości są listy:

d = defaultdict(list, (('a', [1, 2]), ('b', [2, 3]), ('c', [3]), ('d', [4]))) 

Jeśli początkowa danych musi być w forma krotek, której drugi element jest zawsze liczbą całkowitą, a następnie po prostu przejdź do pętli for. Nazywasz to jednym dodatkowym krokiem; Nazywam to jasnym i oczywistym sposobem na zrobienie tego.

+0

+1. BTW, inicjalizując go jako 'defaultdict (list, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})' również Prace. – ChaimG

3

Myślę, że większość tego jest dużo dymu i luster, aby uniknąć prostego for pętlę:

di={} 
for k,v in [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),('b', 3)]: 
    di.setdefault(k,[]).append(v) 
# di={'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]} 

Jeśli twoim celem jest jedna linia i chcesz składni obraźliwy że nie można w ogóle popiera ani obsługiwać można wykorzystać efekt uboczny zrozumieniem:

>>> li=[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),('b', 3)] 
>>> di={};{di.setdefault(k[0],[]).append(k[1]) for k in li} 
set([None]) 
>>> di 
{'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]} 

Jeśli naprawdę chce iść za burtę na nieczytelny:

>>> {k1:[e for _,e in v1] for k1,v1 in {k:filter(lambda x: x[0]==k,li) for k,v in li}.items()} 
{'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]} 

Nie chcesz tego robić. Użyj pętli for Luke!

1
>>> kvs = [(1,2), (2,3), (1,3)] 
>>> reduce(
... lambda d,(k,v): d[k].append(v) or d, 
... kvs, 
... defaultdict(list)) 
defaultdict(<type 'list'>, {1: [2, 3], 2: [3]})