¿Cómo funciona collections.defaultdict?

He leído los ejemplos en documentos de Python, pero aún no puedo entender qué significa este método. ¿Alguien puede ayudar? Aquí hay dos ejemplos de los documentos de Python

>>> from collections import defaultdict >>> s = 'mississippi' >>> d = defaultdict(int) >>> for k in s: ... d[k] += 1 ... >>> d.items() [('i', 4), ('p', 2), ('s', 4), ('m', 1)] 

y

 >>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)] >>> d = defaultdict(list) >>> for k, v in s: ... d[k].append(v) ... >>> d.items() [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])] 

Los parámetros int y list son para que?

Por lo general, un diccionario de Python lanza un KeyError si intenta obtener un elemento con una clave que no está actualmente en el diccionario. Por el contrario, el defaultdict simplemente creará los elementos a los que intenta acceder (siempre que, por supuesto, aún no existan). Para crear un elemento “predeterminado” de este tipo, llama al objeto de función que le pasa al constructor (más precisamente, es un objeto “llamable” arbitrario, que incluye objetos de tipo y función). Para el primer ejemplo, los elementos predeterminados se crean utilizando int() , que devolverá el objeto entero 0 . Para el segundo ejemplo, los elementos predeterminados se crean utilizando list() , que devuelve un nuevo objeto de lista vacía.

defaultdict significa que si no se encuentra una clave en el diccionario, en lugar de que se KeyError un KeyError , se crea una nueva entrada. El tipo de esta nueva entrada viene dado por el argumento de defaultdict.

Por ejemplo:

 somedict = {} print(somedict[3]) # KeyError someddict = defaultdict(int) print(someddict[3]) # print int(), thus 0 

sentencia por defecto

“El diccionario estándar incluye el método setdefault () para recuperar un valor y establecer un valor predeterminado si el valor no existe. Por el contrario, defaultdict permite a la persona que llama especificar el valor predeterminado (valor a devolver) al inicio cuando se inicializa el contenedor”.

como lo define Doug Hellmann en The Python Standard Library por ejemplo

Cómo usar defaultdict

Importar defaultdict

 >>> from collections import defaultdict 

Inicializar defaultdict

Inicializarlo pasando

invocable como su primer argumento (obligatorio)

 >>> d_int = defaultdict(int) >>> d_list = defaultdict(list) >>> def foo(): ... return 'default value' ... >>> d_foo = defaultdict(foo) >>> d_int defaultdict(, {}) >>> d_list defaultdict(, {}) >>> d_foo defaultdict(, {}) 

** kwargs como su segundo argumento (opcional)

 >>> d_int = defaultdict(int, a=10, b=12, c=13) >>> d_int defaultdict(, {'a': 10, 'c': 13, 'b': 12}) 

o

 >>> kwargs = {'a':10,'b':12,'c':13} >>> d_int = defaultdict(int, **kwargs) >>> d_int defaultdict(, {'a': 10, 'c': 13, 'b': 12}) 

Cómo funciona

Como es una clase secundaria de diccionario estándar, puede realizar todas las mismas funciones.

Pero en caso de pasar una clave desconocida, devuelve el valor predeterminado en lugar de error. Por ejemplo:

 >>> d_int['a'] 10 >>> d_int['d'] 0 >>> d_int defaultdict(, {'a': 10, 'c': 13, 'b': 12, 'd': 0}) 

En caso de que desee cambiar el valor predeterminado, sobrescriba default_factory:

 >>> d_int.default_factory = lambda: 1 >>> d_int['e'] 1 >>> d_int defaultdict( at 0x7f34a0a91578>, {'a': 10, 'c': 13, 'b': 12, 'e': 1, 'd': 0}) 

o

 >>> def foo(): ... return 2 >>> d_int.default_factory = foo >>> d_int['f'] 2 >>> d_int defaultdict(, {'a': 10, 'c': 13, 'b': 12, 'e': 1, 'd': 0, 'f': 2}) 

Ejemplos en la pregunta

Ejemplo 1

Como int se ha pasado como default_factory, cualquier clave desconocida devolverá 0 de forma predeterminada.

Ahora, a medida que se pasa la cadena en el bucle, boostá la cuenta de esos alfabetos en d.

 >>> s = 'mississippi' >>> d = defaultdict(int) >>> d.default_factory  >>> for k in s: ... d[k] += 1 >>> d.items() [('i', 4), ('p', 2), ('s', 4), ('m', 1)] >>> d defaultdict(, {'i': 4, 'p': 2, 's': 4, 'm': 1}) 

Ejemplo 2

Como una lista se ha pasado como default_factory, cualquier clave desconocida (no existente) devolverá [] (es decir, lista) de forma predeterminada.

Ahora, a medida que la lista de tuplas se pasa en el bucle, agregará el valor en el d [color]

 >>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)] >>> d = defaultdict(list) >>> d.default_factory  >>> for k, v in s: ... d[k].append(v) >>> d.items() [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])] >>> d defaultdict(, {'blue': [2, 4], 'red': [1], 'yellow': [1, 3]}) 

Aquí hay una gran explicación de los defaultdicts: http://ludovf.net/blog/python-collections-defaultdict/

Básicamente, los parámetros int y list son funciones que se pasan. Recuerda que Python acepta nombres de funciones como argumentos. int devuelve 0 de forma predeterminada y la lista devuelve una lista vacía cuando se llama entre paréntesis.

En los diccionarios normales, si en su ejemplo bash llamar d[a] , obtendré un error (KeyError), ya que solo existen las teclas m, s, iyp y la tecla a no se ha inicializado. Pero en un punto predeterminado, toma el nombre de una función como un argumento, cuando intenta usar una clave que no se ha inicializado, simplemente llama a la función que ha pasado y asigna su valor de retorno como el valor de la nueva clave.

Los diccionarios son una forma conveniente de almacenar datos para su posterior recuperación por nombre (clave). Las claves deben ser objetos únicos e inmutables, y suelen ser cadenas. Los valores en un diccionario pueden ser cualquier cosa. Para muchas aplicaciones, los valores son tipos simples como enteros y cadenas.

Se vuelve más interesante cuando los valores en un diccionario son colecciones (listas, dictados, etc.) En este caso, el valor (una lista vacía o dict) debe inicializarse la primera vez que se usa una clave determinada. Si bien esto es relativamente fácil de hacer manualmente, el tipo defaultdict automatiza y simplifica este tipo de operaciones. Un fallo predeterminado funciona exactamente igual que un dict normal, pero se inicializa con una función (“fábrica predeterminada”) que no toma argumentos y proporciona el valor predeterminado para una clave inexistente.

Un defaultdict nunca generará un KeyError. Cualquier clave que no exista obtiene el valor devuelto por la fábrica predeterminada.

 from collections import defaultdict ice_cream = defaultdict(lambda: 'Vanilla') ice_cream = defaultdict(lambda: 'Vanilla') ice_cream['Sarah'] = 'Chunky Monkey' ice_cream['Abdul'] = 'Butter Pecan' print(ice_cream['Sarah']) >>>Chunky Monkey print(ice_cream['Joe']) >>>Vanilla 

Aquí hay otro ejemplo. Cómo usar defaultdict cómo podemos reducir la complejidad

 from collections import defaultdict # Time complexity O(n^2) def delete_nth_naive(array, n): ans = [] for num in array: if ans.count(num) < n: ans.append(num) return ans # Time Complexity O(n), using hash tables. def delete_nth(array,n): result = [] counts = defaultdict(int) for i in array: if counts[i] < n: result.append(i) counts[i] += 1 return result x = [1,2,3,1,2,1,2,3] print(delete_nth(x, n=2)) print(delete_nth_naive(x, n=2)) 

En conclusión, siempre que necesite un diccionario, y el valor de cada elemento debe comenzar con un valor predeterminado, use un valor predeterminado.

Mi propio 2 ¢: también puedes subclase defaultdict:

 class MyDict(defaultdict): def __missing__(self, key): value = [None, None] self[key] = value return value 

Esto podría ser útil para casos muy complejos.

Dado que la pregunta es sobre “cómo funciona”, algunos lectores pueden querer ver más tuercas y tornillos. Específicamente, el método en cuestión es el __missing__(key) . Consulte: https://docs.python.org/2/library/collections.html#defaultdict-objects .

Más concretamente, esta respuesta muestra cómo utilizar __missing__(key) de una manera práctica: https://stackoverflow.com/a/17956989/1593924

Para aclarar lo que significa ‘callable’, aquí hay una sesión interactiva (de 2.7.6, pero también debería funcionar en v3):

 >>> x = int >>> x  >>> y = int(5) >>> y 5 >>> z = x(5) >>> z 5 >>> from collections import defaultdict >>> dd = defaultdict(int) >>> dd defaultdict(, {}) >>> dd = defaultdict(x) >>> dd defaultdict(, {}) >>> dd['a'] 0 >>> dd defaultdict(, {'a': 0}) 

Ese fue el uso más típico de defaultdict (excepto por el uso sin sentido de la variable x). Puede hacer lo mismo con 0 como el valor predeterminado explícito, pero no con un valor simple:

 >>> dd2 = defaultdict(0) Traceback (most recent call last): File "", line 1, in  dd2 = defaultdict(0) TypeError: first argument must be callable 

En cambio, lo siguiente funciona porque pasa en una función simple (crea sobre la marcha una función sin nombre que no toma argumentos y siempre devuelve 0):

 >>> dd2 = defaultdict(lambda: 0) >>> dd2 defaultdict( at 0x02C4C130>, {}) >>> dd2['a'] 0 >>> dd2 defaultdict( at 0x02C4C130>, {'a': 0}) >>> 

Y con un valor por defecto diferente:

 >>> dd3 = defaultdict(lambda: 1) >>> dd3 defaultdict( at 0x02C4C170>, {}) >>> dd3['a'] 1 >>> dd3 defaultdict( at 0x02C4C170>, {'a': 1}) >>> 

Sin el defaultdict , probablemente pueda asignar nuevos valores a las claves que no se defaultdict , pero no puede modificarlo. Por ejemplo:

 import collections d = collections.defaultdict(int) for i in range(10): d[i] += i print(d) # Output: defaultdict(, {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}) import collections d = {} for i in range(10): d[i] += i print(d) # Output: Traceback (most recent call last): File "python", line 4, in  KeyError: 0 

Creo que es mejor utilizarlo en lugar de una statement de cambio de caso. Imagínese si tenemos una statement de cambio de caso como la siguiente:

 option = 1 switch(option) { case 1: print '1st option' case 2: print '2nd option' case 3: print '3rd option' default: return 'No such option' } 

No hay declaraciones de casos de switch disponibles en Python. Podemos lograr lo mismo usando defaultdict .

 from collections import defaultdict def default_value(): return "Default Value" dd = defaultdict(default_value) dd[1] = '1st option' dd[2] = '2nd option' dd[3] = '3rd option' print(dd[4]) print(dd[5]) print(dd[3]) 

Se imprime:

 Default Value Default Value 3rd option 

En el fragmento de dd anterior, dd no tiene teclas 4 o 5 y, por lo tanto, imprime un valor predeterminado que hemos configurado en una función auxiliar. Esto es bastante mejor que un diccionario sin KeyError donde se lanza un KeyError clave si la clave no está presente. A partir de esto, es evidente que defaultdict parece más a una statement de caso de cambio donde podemos evitar los complicados bloques if-elif-elif-else .

Otro buen ejemplo que me impresionó mucho de este sitio es:

 >>> from collections import defaultdict >>> food_list = 'spam spam spam spam spam spam eggs spam'.split() >>> food_count = defaultdict(int) # default value of int is 0 >>> for food in food_list: ... food_count[food] += 1 # increment element's value by 1 ... defaultdict(, {'eggs': 1, 'spam': 7}) >>> 

Si intentamos acceder a cualquier elemento que no sea eggs y spam no spam , obtendremos un recuento de 0.

La herramienta defaultdict es un contenedor en la clase de colecciones de Python. Es similar al contenedor de diccionario (dict) habitual, pero tiene una diferencia: el tipo de datos de los campos de valor se especifica en la inicialización.

Por ejemplo:

 from collections import defaultdict d = defaultdict(list) d['python'].append("awesome") d['something-else'].append("not relevant") d['python'].append("language") for i in d.items(): print i 

Esto imprime:

 ('python', ['awesome', 'language']) ('something-else', ['not relevant']) 

Bueno, defaultdict también puede boost el error clave en el siguiente caso:

  from collections import defaultdict d = defaultdict() print(d[3]) #raises keyerror 

Recuerda siempre dar argumentos al defaultdict como defaultdict (int).

El diccionario estándar incluye el método setdefault () para recuperar un valor y establecer un valor predeterminado si el valor no existe. Por el contrario, defaultdict le permite a la persona que llama especificar el valor predeterminado por adelantado cuando se inicializa el contenedor.

 import collections def default_factory(): return 'default value' d = collections.defaultdict(default_factory, foo='bar') print 'd:', d print 'foo =>', d['foo'] print 'bar =>', d['bar'] 

Esto funciona bien siempre y cuando sea apropiado que todas las claves tengan el mismo valor predeterminado. Puede ser especialmente útil si el valor predeterminado es un tipo utilizado para agregar o acumular valores, como una lista, un conjunto o incluso un int. La documentación estándar de la biblioteca incluye varios ejemplos del uso de defaultdict de esta manera.

 $ python collections_defaultdict.py d: defaultdict(, {'foo': 'bar'}) foo => bar bar => default value 

En breve:

defaultdict(int) : el argumento int indica que los valores serán tipo int.

defaultdict(list) : la lista de argumentos indica que los valores serán de tipo de lista.

La documentación y la explicación son bastante autoexplicativas:

http://docs.python.org/library/collections.html#collections.defaultdict

La función de tipo (int / str, etc.) pasada como argumento se usa para inicializar un valor predeterminado para cualquier clave dada donde la clave no está presente en el dict.