Lanston
He leído los ejemplos en documentos de python, pero todavía 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()
dict_items([('m', 1), ('i', 4), ('s', 4), ('p', 2)])
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 parametros int
y list
son para que?
Sven Marnach
Por lo general, un diccionario de Python arroja un KeyError
si intenta obtener un elemento con una clave que no se encuentra actualmente en el diccionario. El defaultdict
por el contrario, simplemente creará cualquier elemento al que intente acceder (siempre que, por supuesto, aún no exista). Para crear un elemento “predeterminado” de este tipo, llama al objeto de función que pasa al constructor (más precisamente, es un objeto “invocable” arbitrario, que incluye objetos de tipo y función). Para el primer ejemplo, los elementos predeterminados se crean usando 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.
-
¿Es funcionalmente diferente que usar d.get(key, default_val) ?
– Ambareesh
1 de mayo de 2019 a las 1:31
-
@Ambareesh
d.get(key, default)
nunca modificará su diccionario, simplemente devolverá el valor predeterminado y dejará el diccionario sin cambios.defaultdict
por otro lado, será insertar una clave en el diccionario si aún no está allí. Esta es una gran diferencia; vea los ejemplos en la pregunta para entender por qué.– Sven Marnach
2 mayo 2019 a las 19:10
-
¿Cómo sabemos cuál es el valor predeterminado para cada tipo? 0 para int() y [] for list() son intuitivos, pero también puede haber tipos más complejos o autodefinidos.
– sean
11 de marzo de 2020 a las 10:40
-
@Sean
defaultdict
llama a cualquier constructor que le pases. Si le pasas un tipoT
los valores se construirán usandoT()
. No todos los tipos se pueden construir sin pasar ningún parámetro. Si desea construir un tipo de este tipo, necesita una función contenedora, o algo asífunctools.partial(T, arg1, arg2)
.– Sven Marnach
11 de marzo de 2020 a las 10:58
-
O aún más fácilmente: una lambda.
defaultdict(lambda : T(arg1, arg2))
.– Mees de Vries
18 de agosto de 2020 a las 11:24
orlp
defaultdict
significa que si una clave no se encuentra en el diccionario, entonces en lugar de una KeyError
siendo lanzado, 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
-
“El tipo de este nuevo par viene dado por el argumento de defaultdict”. Tenga en cuenta que el argumento puede ser cualquier objeto invocable, no solo funciones de tipo. Por ejemplo, si foo fuera una función que devolviera “barra”, foo podría usarse como argumento para el dictado predeterminado y si se accedió a una clave no presente, su valor se establecería en “barra”.
– lf215
29 de julio de 2013 a las 5:56
-
O si solo desea devolver “barra”: somedict = defaultdict(lambda:”bar”)
–Michael Scott Asato Cuthbert
2 jun 2014 a las 21:23
-
Cuarta línea devuelta
0
el entero, si fuerasomeddict = defaultdict(list)
vuelve[ ]
. ¿Es 0 el número entero predeterminado? O [ ] la lista predeterminada?– Gathide
5 de enero de 2017 a las 7:30
-
Ni.
0
es inmutable – en CPython todos los valores de-5
a256
son singletons almacenados en caché, pero este es un comportamiento específico de la implementación; en ambos casos, se “crea” una nueva instancia cada vez conint()
olist()
. De esa manera,d[k].append(v)
puede funcionar sin llenar el diccionario con referencias a la misma lista, lo que representaríadefaultdict
casi inútil Si este fuera el comportamiento,defaultdict
tomaría un valor, no una lambda, como parámetro. (¡Perdón por la terrible explicación!)– wizzwizz4
7 de octubre de 2017 a las 8:58
Somendra Joshi
predeterminadodict
“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 que la persona que llama especifique el valor predeterminado (valor que se devolverá) por adelantado cuando se inicializa el contenedor”.
Según lo definido por Doug Hellmann en La biblioteca estándar de Python por ejemplo
Cómo usar el dictado predeterminado
Importar dictamen predeterminado
>>> from collections import defaultdict
Inicializar dictado predeterminado
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(<type 'int'>, {})
>>> d_list
defaultdict(<type 'list'>, {})
>>> d_foo
defaultdict(<function foo at 0x7f34a0a69578>, {})
**kwargs como su segundo argumento (opcional)
>>> d_int = defaultdict(int, a=10, b=12, c=13)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})
o
>>> kwargs = {'a':10,'b':12,'c':13}
>>> d_int = defaultdict(int, **kwargs)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})
Cómo funciona
Como es una clase secundaria del 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(<type 'int'>, {'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(<function <lambda> 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(<function foo at 0x7f34a0a0a140>, {'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, aumentará el recuento de esos alfabetos en d.
>>> s="mississippi"
>>> d = defaultdict(int)
>>> d.default_factory
<type 'int'>
>>> for k in s:
... d[k] += 1
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]
>>> d
defaultdict(<type 'int'>, {'i': 4, 'p': 2, 's': 4, 'm': 1})
Ejemplo 2
Como se ha pasado una lista como default_factory, cualquier clave desconocida (inexistente) regresará [ ](es decir, lista) por defecto.
Ahora, a medida que la lista de tuplas se pasa en el ciclo, agregará el valor en el d[color]
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> d.default_factory
<type 'list'>
>>> for k, v in s:
... d[k].append(v)
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
>>> d
defaultdict(<type 'list'>, {'blue': [2, 4], 'red': [1], 'yellow': [1, 3]})
-
Gracias por la respuesta. ¿Sabes cómo hacer que la constante sea siempre diferente? Yo explico:
defaultdict(lambda: 'string', **kwargs)
no funcionará como se esperaba porque todas las claves nuevas compartirán la misma instancia de ‘cadena’. ¿Cómo puedo proporcionar una copia cada vez? Tenga en cuenta quedefaultdict(lambda: copy.copy('string'), **kwargs)
no funciona porque la copia se evalúa solo una vez.– Dr_Zaszuś
23 de junio de 2020 a las 9:48
dimensión
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, por lo general, son 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 dictado) 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 dictado predeterminado funciona exactamente como un dictado normal, pero se inicializa con una función (“fábrica predeterminada”) que no acepta argumentos y proporciona el valor predeterminado para una clave inexistente.
Un dictado predeterminado nunca generará un KeyError. Cualquier clave que no existe obtiene el valor devuelto por la fábrica predeterminada.
from collections import defaultdict
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 sobre cómo usar defaultdict, 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, cada vez que necesite un diccionario, y el valor de cada elemento debe comenzar con un valor predeterminado, use un diccionario predeterminado.
varagraval
Hay una gran explicación de los dictados predeterminados aquí: http://ludovf.net/blog/python-collections-defaultdict/
Básicamente, los parámetros En t y lista son funciones que te pasan. Recuerda que Python acepta nombres de funciones como argumentos. En t devuelve 0 por defecto y lista devuelve una lista vacía cuando se llama entre paréntesis.
En diccionarios normales, si en tu ejemplo intento llamar d[a]
, obtendré un error (KeyError), ya que solo existen las teclas m, s, i y p y la tecla a no se ha inicializado. Pero en un dictado predeterminado, toma un nombre de función como argumento, cuando intenta usar una clave que no se ha inicializado, simplemente llama a la función que pasó y asigna su valor de retorno como el valor de la nueva clave.
Diego Queiroz
el comportamiento de defaultdict
se puede imitar fácilmente usando dict.setdefault
en lugar de d[key]
en cada llamada.
En otras palabras, el código:
from collections import defaultdict
d = defaultdict(list)
print(d['key']) # empty list []
d['key'].append(1) # adding constant 1 to the list
print(d['key']) # list containing the constant [1]
es equivalente a:
d = dict()
print(d.setdefault('key', list())) # empty list []
d.setdefault('key', list()).append(1) # adding constant 1 to the list
print(d.setdefault('key', list())) # list containing the constant [1]
La única diferencia es que, usando defaultdict
el constructor de listas se llama solo una vez, y usando dict.setdefault
el constructor de la lista se llama con más frecuencia (pero el código se puede reescribir para evitar esto, si es realmente necesario).
Algunos pueden argumentar que hay una consideración de rendimiento, pero este tema es un campo minado. Esta publicación muestra que no hay una gran ganancia de rendimiento al usar defaultdict, por ejemplo.
En mi opinión, defaultdict es una colección que agrega más confusión que beneficios al código. Inútil para mí, pero otros pueden pensar diferente.
Comunidad
Dado que la pregunta es sobre “cómo funciona”, algunos lectores pueden querer ver más detalles. En concreto, el método en cuestión es el __missing__(key)
método. Ver: https://docs.python.org/2/library/collections.html#defaultdict-objects .
Más concretamente, esta respuesta muestra cómo hacer uso de __missing__(key)
de forma práctica: https://stackoverflow.com/a/17956989/1593924
Para aclarar qué significa ‘llamable’, aquí hay una sesión interactiva (de 2.7.6 pero también debería funcionar en v3):
>>> x = int
>>> x
<type 'int'>
>>> y = int(5)
>>> y
5
>>> z = x(5)
>>> z
5
>>> from collections import defaultdict
>>> dd = defaultdict(int)
>>> dd
defaultdict(<type 'int'>, {})
>>> dd = defaultdict(x)
>>> dd
defaultdict(<type 'int'>, {})
>>> dd['a']
0
>>> dd
defaultdict(<type 'int'>, {'a': 0})
Ese fue el uso más típico de defaultdict (excepto por el uso inútil de la variable x). Puede hacer lo mismo con 0 como valor predeterminado explícito, pero no con un valor simple:
>>> dd2 = defaultdict(0)
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
dd2 = defaultdict(0)
TypeError: first argument must be callable
En cambio, lo siguiente funciona porque pasa una función simple (crea sobre la marcha una función sin nombre que no acepta argumentos y siempre devuelve 0):
>>> dd2 = defaultdict(lambda: 0)
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {})
>>> dd2['a']
0
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {'a': 0})
>>>
Y con un valor predeterminado diferente:
>>> dd3 = defaultdict(lambda: 1)
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {})
>>> dd3['a']
1
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {'a': 1})
>>>
Por cierto, dependiendo de su caso de uso, no lo olvide para congelar el dictamen predeterminado para uso de solo lectura configurando su
default_factory = None
después de que haya terminado de completar el dictado predeterminado. Ver esta pregunta.– Asclepio
30 de octubre de 2016 a las 5:54
Ver también: stackoverflow.com/questions/17215400/…
– drftymac
10/10/2017 a las 21:05