2013-08-29 5 views
11

Es wäre bequemer, wenn ein defaultdictdefaultdict Ein-Schritt-Initialisierung

entlang der folgenden Zeilen initialisiert werden konnte
d = defaultdict(list, (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), 
    ('b', 3))) 

zu produzieren

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

Stattdessen bekomme ich

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

zu bekomme, was ich brauche, muss ich das tun:

d = defaultdict(list) 
for x, y in (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)): 
    d[x].append(y) 

Das ist IMO ein Schritt mehr als nötig sein sollte, fehlt mir hier etwas?

+0

Wie würde es wissen, Append auf der Liste zu verwenden? Und was ist mit anderen Arten? –

+0

@ JonClements, guter Punkt. Man könnte jedoch denken, dass "Liste" ein häufig genug verwendeter Fall ist, dass eine Bequemlichkeitsmethode (vielleicht eine Klassenmethode) gerechtfertigt ist? – iruvar

+3

Wäre diese Bequemlichkeitsmethode nicht genau das, was Sie gerade am Ende Ihres Posts geschrieben haben? Warum nicht diese drei Zeilen in eine Funktion einpacken und es einen Tag nennen? –

Antwort

9

Das Verhalten, das Sie beschreiben, wäre nicht konsistent mit den anderen Verhaltensweisen defaultdict.Scheint, wie Sie wollen, was FooDict ist so, dass

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

Wir tun können, aber nicht mit defaultdict; es AppendDict

nennen wir
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

Sortier- und itertools.groupby ein langer Weg:

>>> 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]}) 

Um dieses eher ein Einzeiler zu machen:

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])]) 

this helps

+4

Wenn das OP nicht eine vollkommen feine "for" -Schleife mag, bezweifle ich, dass 'itertools.groupby', eine' sort', eine liste comp und entweder 'lambda' oder' itemgetter' ansprechend sind. – DSM

+0

Interessant. Aber beachte, dass die 'sort' und die' groupby' die ganze Leg-Arbeit hier erledigen. so dass Sie genauso einfach die Ausgabe von 'groupby' an ein normales' dict' anstatt an ein 'defaultdict' füttern könnten! – iruvar

+0

@ 1_CR: Sie haben Recht. Aber ich habe dir einen Standardbefehl gegeben, weil du nach einem gefragt hast. – inspectorG4dget

13

Was bist du anscheinend fehlt dass defaultdict eine einfache (nicht besonders "magische") Unterklasse von dict ist. Das erste Argument enthält eine Factory-Funktion für fehlende Schlüssel. Wenn Sie eine defaultdict initialisieren, initialisieren Sie eine dict.

Wenn Sie

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

produzieren wollen, sollten Sie es die Art und Weise werden die Initialisierung Sie würden initialisieren andere dict, deren Werte Listen:

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

Wenn Ihre Ausgangsdaten hat in der sein Form von Tupeln, deren zweites Element immer eine Ganzzahl ist, dann geh einfach mit der for Schleife. Du nennst es einen zusätzlichen Schritt; Ich nenne das den klaren und offensichtlichen Weg, es zu tun.

+0

+1 plappern. BTW, initialisiere es als 'defaultdict (liste, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})' auch funktioniert. – ChaimG

3

Ich denke, die meisten davon ist eine Menge Rauch und Spiegel eine einfache for Schleife zu vermeiden:

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]} 

Wenn Ihr Ziel ist eine Zeile ist, und Sie missbräuchlichen Syntax möchten, dass ich unterstützt oder unterstützen können nicht alle können Sie eine Nebenwirkung Verständnis verwenden:

>>> 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]} 

Wenn Sie wirklich wollen, über Bord gehen in die unlesbar:

>>> {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]} 

Sie wollen das nicht tun. Benutze die for-Schleife 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]}) 
Verwandte Themen