functions - python dictionary methods
dict python: get vs setdefault (6)
1. Explicado con un buen ejemplo aquí:
http://code.activestate.com/recipes/66516-add-an-entry-to-a-dictionary-unless-the-entry-is-a/
dict. uso típico de setdefault
somedict.setdefault(somekey,[]).append(somevalue)
dict. obtener el uso típico
theIndex[word] = 1 + theIndex.get(word,0)
2. Más explicaciones: http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html
dict.setdefault()
es equivalente a get
o set & get
. O set if necessary then get
. Es especialmente eficiente si su clave de diccionario es costosa de computar o larga de escribir.
El único problema con dict.setdefault () es que el valor predeterminado siempre se evalúa, si es necesario o no. Eso solo importa si el valor predeterminado es costoso de calcular . En ese caso, use defaultdict.
3. Finalmente, los documentos oficiales con diferencia resaltaron http://docs.python.org/2/library/stdtypes.html
get(key[, default])
Devuelve el valor de la clave si la clave está en el diccionario, de lo contrario, será el valor predeterminado. Si no se proporciona el valor predeterminado, su valor predeterminado es Ninguno, por lo que este método nunca genera un KeyError.
setdefault(key[, default])
Si la clave está en el diccionario, devuelve su valor. De lo contrario, inserte la clave con un valor predeterminado y devuelva el valor predeterminado. el valor predeterminado es Ninguno.
Las siguientes dos expresiones me parecen equivalentes. ¿Cuál es preferible?
data = [(''a'', 1), (''b'', 1), (''b'', 2)]
d1 = {}
d2 = {}
for key, val in data:
# variant 1)
d1[key] = d1.get(key, []) + [val]
# variant 2)
d2.setdefault(key, []).append(val)
Los resultados son los mismos, pero ¿qué versión es mejor o más bien pitonica?
Personalmente encuentro que la versión 2 es más difícil de entender, ya que para mí el setdefault es muy difícil de entender. Si lo entiendo correctamente, busca el valor de "clave" en el diccionario, si no está disponible, ingresa "[]" en el diccionario, devuelve una referencia al valor o "[]" y agrega "val" a ese referencia. Si bien es sencillo, no es intuitivo en absoluto (al menos para mí).
En mi opinión, la versión 1 es más fácil de entender (si está disponible, obtenga el valor de "clave", si no, obtenga "[]", luego únase a una lista compuesta de [val] y coloque el resultado en "clave" ) Pero aunque es más intuitivo de entender, me temo que esta versión es menos eficiente, con toda esta lista creando. Otra desventaja es que "d1" aparece dos veces en la expresión, que es bastante propensa a errores. Probablemente hay una mejor implementación usando get, pero en este momento me elude.
Mi suposición es que la versión 2, aunque es más difícil de comprender para los inexpertos, es más rápida y, por lo tanto, preferible. Opiniones?
Es posible que desee ver defaultdict
en el módulo de collections
. Lo siguiente es equivalente a tus ejemplos.
from collections import defaultdict
data = [(''a'', 1), (''b'', 1), (''b'', 2)]
d = defaultdict(list)
for k, v in data:
d[k].append(v)
Hay más here .
La lógica de dict.get
es:
if key in a_dict:
value = a_dict[key]
else:
value = default_value
Tome un ejemplo:
In [72]: a_dict = {''mapping'':[''dict'', ''OrderedDict''], ''array'':[''list'', ''tuple'']}
In [73]: a_dict.get(''string'', [''str'', ''bytes''])
Out[73]: [''str'', ''bytes'']
In [74]: a_dict.get(''array'', [''str'', ''byets''])
Out[74]: [''list'', ''tuple'']
El mecamismo de setdefault
es:
levels = [''master'', ''manager'', ''salesman'', ''accountant'', ''assistant'']
#group them by the leading letter
group_by_leading_letter = {}
# the logic expressed by obvious if condition
for level in levels:
leading_letter = level[0]
if leading_letter not in group_by_leading_letter:
group_by_leading_letter[leading_letter] = [level]
else:
group_by_leading_letter[leading_letter].append(word)
In [80]: group_by_leading_letter
Out[80]: {''a'': [''accountant'', ''assistant''], ''m'': [''master'', ''manager''], ''s'': [''salesman'']}
El método setdefault dict es precisamente para este propósito. El ciclo for anterior se puede reescribir como sigue:
In [87]: for level in levels:
...: leading = level[0]
...: group_by_leading_letter.setdefault(leading,[]).append(level)
Out[80]: {''a'': [''accountant'', ''assistant''], ''m'': [''master'', ''manager''], ''s'': [''salesman'']}
Es muy simple, significa que una lista no nula agrega un elemento o una lista nula anexa un elemento.
El defaultdict
, que hace que esto sea aún más fácil. Para crear uno, pasas un tipo o función para generar el valor predeterminado para cada ranura en el dict:
from collections import defualtdict
group_by_leading_letter = defaultdict(list)
for level in levels:
group_by_leading_letter[level[0]].append(level)
La respuesta aceptada de agf no es comparar con like. Después:
print timeit("d[0] = d.get(0, []) + [1]", "d = {1: []}", number = 10000)
d[0]
contiene una lista con 10.000 elementos, mientras que después:
print timeit("d.setdefault(0, []) + [1]", "d = {1: []}", number = 10000)
d[0]
es simplemente []
. es decir, la versión d.setdefault
nunca modifica la lista almacenada en d
. El código debería ser:
print timeit("d.setdefault(0, []).append(1)", "d = {1: []}", number = 10000)
y de hecho es más rápido que el ejemplo de setdefault
defectuoso.
La diferencia aquí se debe a que al anexar usando concatenación, la lista completa se copia todas las veces (y una vez que tiene 10,000 elementos que comienzan a medirse.) Usando append
las actualizaciones de la lista se amortizan O (1), es decir, tiempo efectivamente constante.
Finalmente, hay otras dos opciones no consideradas en la pregunta original: defaultdict
o simplemente probar el diccionario para ver si ya contiene la clave.
Entonces, asumiendo d3, d4 = defaultdict(list), {}
# variant 1 (0.39)
d1[key] = d1.get(key, []) + [val]
# variant 2 (0.003)
d2.setdefault(key, []).append(val)
# variant 3 (0.0017)
d3[key].append(val)
# variant 4 (0.002)
if key in d4:
d4[key].append(val)
else:
d4[key] = [val]
la variante 1 es de lejos la más lenta porque copia la lista cada vez, la variante 2 es la segunda más lenta, la variante 3 es la más rápida pero no funcionará si necesita Python anterior a 2.5, y la variante 4 es ligeramente más lenta que la variante 3 .
Yo diría que use la variante 3 si puede, con la variante 4 como una opción para aquellos lugares ocasionales en los que el defaultdict
no es un ajuste exacto. Evita ambas de tus variantes originales.
Sus dos ejemplos hacen lo mismo, pero eso no significa get
y setdefault
do.
La diferencia entre los dos es, básicamente, el ajuste manual de d[key]
para apuntar a la lista cada vez, frente a setdefault
automáticamente configurando d[key]
a la lista solo cuando está desactivada.
Haciendo los dos métodos lo más similares posible, corrí
from timeit import timeit
print timeit("c = d.get(0, []); c.extend([1]); d[0] = c", "d = {1: []}", number = 1000000)
print timeit("c = d.get(1, []); c.extend([1]); d[0] = c", "d = {1: []}", number = 1000000)
print timeit("d.setdefault(0, []).extend([1])", "d = {1: []}", number = 1000000)
print timeit("d.setdefault(1, []).extend([1])", "d = {1: []}", number = 1000000)
y consiguió
0.794723378711
0.811882272256
0.724429205999
0.722129751973
Entonces setdefault
es alrededor de un 10% más rápido que get
para este propósito.
El método get
te permite hacer menos de lo que puedes con setdefault
. Puede usarlo para evitar obtener un KeyError
cuando la clave no existe (si eso es algo que va a suceder con frecuencia), incluso si no desea establecer la clave.
Consulte los casos de uso para el método dict ''setdefault'' y el método dict.get () devuelve un puntero para obtener más información sobre los dos métodos.
El hilo sobre setdefault
concluye que la mayoría de las veces, quiere usar un defaultdict
. El hilo sobre get
concluye que es lento, y a menudo es mejor (velocidad) haciendo una doble búsqueda, usando un default, o manejando el error (dependiendo del tamaño del diccionario y su caso de uso).
In [1]: person_dict = {}
In [2]: person_dict[''liqi''] = ''LiQi''
In [3]: person_dict.setdefault(''liqi'', ''Liqi'')
Out[3]: ''LiQi''
In [4]: person_dict.setdefault(''Kim'', ''kim'')
Out[4]: ''kim''
In [5]: person_dict
Out[5]: {''Kim'': ''kim'', ''liqi'': ''LiQi''}
In [8]: person_dict.get(''Dim'', '''')
Out[8]: ''''
In [5]: person_dict
Out[5]: {''Kim'': ''kim'', ''liqi'': ''LiQi''}