¿Inicialización de un solo paso del valor predeterminado que se anexa a la lista?

Sería conveniente si un punto por defaultdict pudiera inicializarse en las siguientes líneas

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

para producir

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

En cambio, me sale

 defaultdict(, {'a': 2, 'c': 3, 'b': 3, 'd': 4}) 

Para conseguir lo que necesito, acabo teniendo que hacer esto:

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

Esto es IMO un paso más de lo que debería ser necesario, ¿me estoy perdiendo algo aquí?

el comportamiento que usted describe no sería coherente con los otros comportamientos de defaultdict . Parece que lo que quieres es FooDict tal que

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

Podemos hacer eso, pero no con defaultdict; Llamémoslo 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) 

Lo que aparentemente estás perdiendo es que el defaultdict es una subclase de dict simple (no especialmente “mágica”). Todo lo que el primer argumento hace es proporcionar una función de fábrica para las claves que faltan . Cuando inicializa un valor defaultdict , está inicializando un dict .

Si quieres producir

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

Debería estar inicializándolo de la misma forma que inicializaría cualquier otro dict cuyos valores sean listas:

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

Si sus datos iniciales tienen que estar en forma de tuplas cuyo segundo elemento es siempre un número entero, simplemente vaya con el bucle for . Lo llamas un paso extra; Yo lo llamo la manera clara y obvia de hacerlo.

Clasificación e itertools.groupby recorrer un largo camino:

 >>> 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(, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}) 

Para hacer esto más de una sola línea:

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

Espero que esto ayude

Creo que la mayoría de esto es mucho humo y espejos para evitar un bucle simple para :

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

Si su objective es una línea y desea una syntax abusiva que no puedo respaldar ni apoyar, puede usar una comprensión de los efectos secundarios:

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

Si realmente quieres ir por la borda hacia lo ilegible:

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

No quieres hacer eso. Utilice el bucle for Luke!

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