Diccionario de Python: obtenga una lista de valores para la lista de claves

182

¿Existe una forma integrada / rápida de usar una lista de claves de un diccionario para obtener una lista de los elementos correspondientes?

Por ejemplo tengo:

>>> mydict = {'one': 1, 'two': 2, 'three': 3}
>>> mykeys = ['three', 'one']

¿Cómo puedo usar mykeyspara obtener los valores correspondientes en el diccionario como una lista?

>>> mydict.WHAT_GOES_HERE(mykeys)
[3, 1]
FazJaxton
fuente

Respuestas:

206

Una lista de comprensión parece ser una buena manera de hacer esto:

>>> [mydict[x] for x in mykeys]
[3, 1]
FazJaxton
fuente
1
Si mydictes una llamada de función (que devuelve un dict), entonces llama a la función varias veces, ¿verdad?
endolito
1
@endolith Sí, lo hará
Eric Romrell
108

Un par de otras formas además de list-comp:

  • Crear lista y lanzar excepción si no se encuentra la clave: map(mydict.__getitem__, mykeys)
  • Crear lista con Nonesi no se encuentra la clave:map(mydict.get, mykeys)

Alternativamente, el uso operator.itemgetterpuede devolver una tupla:

from operator import itemgetter
myvalues = itemgetter(*mykeys)(mydict)
# use `list(...)` if list is required

Nota : en Python3, mapdevuelve un iterador en lugar de una lista. Úselo list(map(...))para una lista.

Jon Clements
fuente
54

Una pequeña comparación de velocidad:

Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Dec  7 2015, 14:10:42) [MSC v.1500 64 bit (AMD64)] on win32
In[1]: l = [0,1,2,3,2,3,1,2,0]
In[2]: m = {0:10, 1:11, 2:12, 3:13}
In[3]: %timeit [m[_] for _ in l]  # list comprehension
1000000 loops, best of 3: 762 ns per loop
In[4]: %timeit map(lambda _: m[_], l)  # using 'map'
1000000 loops, best of 3: 1.66 µs per loop
In[5]: %timeit list(m[_] for _ in l)  # a generator expression passed to a list constructor.
1000000 loops, best of 3: 1.65 µs per loop
In[6]: %timeit map(m.__getitem__, l)
The slowest run took 4.01 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 853 ns per loop
In[7]: %timeit map(m.get, l)
1000000 loops, best of 3: 908 ns per loop
In[33]: from operator import itemgetter
In[34]: %timeit list(itemgetter(*l)(m))
The slowest run took 9.26 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 739 ns per loop

Entonces, la comprensión de la lista y la selección de elementos son las formas más rápidas de hacer esto.

ACTUALIZACIÓN: Para grandes listas aleatorias y mapas tuve resultados un poco diferentes:

Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Dec  7 2015, 14:10:42) [MSC v.1500 64 bit (AMD64)] on win32
In[2]: import numpy.random as nprnd
l = nprnd.randint(1000, size=10000)
m = dict([(_, nprnd.rand()) for _ in range(1000)])
from operator import itemgetter
import operator
f = operator.itemgetter(*l)
%timeit f(m)
%timeit list(itemgetter(*l)(m))
%timeit [m[_] for _ in l]  # list comprehension
%timeit map(m.__getitem__, l)
%timeit list(m[_] for _ in l)  # a generator expression passed to a list constructor.
%timeit map(m.get, l)
%timeit map(lambda _: m[_], l)
1000 loops, best of 3: 1.14 ms per loop
1000 loops, best of 3: 1.68 ms per loop
100 loops, best of 3: 2 ms per loop
100 loops, best of 3: 2.05 ms per loop
100 loops, best of 3: 2.19 ms per loop
100 loops, best of 3: 2.53 ms per loop
100 loops, best of 3: 2.9 ms per loop

Así que en este caso el ganador es clara f = operator.itemgetter(*l); f(m), y forastero claro: map(lambda _: m[_], l).

ACTUALIZACIÓN para Python 3.6.4:

import numpy.random as nprnd
l = nprnd.randint(1000, size=10000)
m = dict([(_, nprnd.rand()) for _ in range(1000)])
from operator import itemgetter
import operator
f = operator.itemgetter(*l)
%timeit f(m)
%timeit list(itemgetter(*l)(m))
%timeit [m[_] for _ in l]  # list comprehension
%timeit list(map(m.__getitem__, l))
%timeit list(m[_] for _ in l)  # a generator expression passed to a list constructor.
%timeit list(map(m.get, l))
%timeit list(map(lambda _: m[_], l)
1.66 ms ± 74.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
2.1 ms ± 93.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.58 ms ± 88.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.36 ms ± 60.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.98 ms ± 142 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.7 ms ± 284 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.14 ms ± 62.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Entonces, los resultados para Python 3.6.4 son casi los mismos.

Sklavit
fuente
15

Aquí hay tres formas.

Aumento KeyErrorcuando no se encuentra la clave:

result = [mapping[k] for k in iterable]

Valores predeterminados para claves faltantes.

result = [mapping.get(k, default_value) for k in iterable]

Saltarse las llaves faltantes.

result = [mapping[k] for k in iterable if k in mapping]
OdraEncoded
fuente
found_keys = mapping.keys() & iterableda TypeError: unsupported operand type(s) for &: 'list' and 'list'en python 2.7; `found_keys = [clave para clave en mapping.keys () si la clave es iterable] funciona mejor
NotGaeL
10

Prueba esto:

mydict = {'one': 1, 'two': 2, 'three': 3}
mykeys = ['three', 'one','ten']
newList=[mydict[k] for k in mykeys if k in mydict]
print newList
[3, 1]
Vikram Singh Chandel
fuente
7

Prueba esto:

mydict = {'one': 1, 'two': 2, 'three': 3}
mykeys = ['three', 'one'] # if there are many keys, use a set

[mydict[k] for k in mykeys]
=> [3, 1]
Óscar López
fuente
@PeterDeGlopper estás confundido. items()es preferible, no tiene que hacer una búsqueda adicional, ¡no hay len(mydict)*len(mykeys)operación aquí! (note que estoy usando un set)
Óscar López
@ ÓscarLópez Sí, está inspeccionando cada elemento del diccionario. iteritems no los produce hasta que los necesite, por lo que evita construir una lista intermediaria, pero aún ejecuta 'k en mykeys' (order len (mykeys), ya que es una lista) por cada k en mydict. Completamente innecesariamente, en comparación con la comprensión de la lista más simple que simplemente pasa por encima de mykeys.
Peter DeGlopper
@ inspectorG4dget @PeterDeGlopper la operación de membresía mykeysse amortiza a tiempo constante, estoy usando un conjunto, no una lista
Óscar López
2
La conversión de la lista del OP a un conjunto al menos lo hace lineal, pero sigue siendo lineal en la estructura de datos incorrecta, además de perder el orden. Considere el caso de un diccionario de 10k y 2 teclas en mykeys. Su solución hace 10k pruebas de membresía, en comparación con dos búsquedas de diccionario para la simple comprensión de la lista. En general, parece seguro suponer que la cantidad de claves será menor que la cantidad de elementos del diccionario, y si no es así, su enfoque omitirá elementos repetidos.
Peter DeGlopper
4
new_dict = {x: v for x, v in mydict.items() if x in mykeys}
Pavel Minenkov
fuente
1

Pandas hace esto con mucha elegancia, aunque las comprensiones de la lista siempre serán más técnicamente pitónicas. No tengo tiempo para poner una comparación de velocidad en este momento (volveré más tarde y la pondré):

import pandas as pd
mydict = {'one': 1, 'two': 2, 'three': 3}
mykeys = ['three', 'one']
temp_df = pd.DataFrame().append(mydict)
# You can export DataFrames to a number of formats, using a list here. 
temp_df[mykeys].values[0]
# Returns: array([ 3.,  1.])

# If you want a dict then use this instead:
# temp_df[mykeys].to_dict(orient='records')[0]
# Returns: {'one': 1.0, 'three': 3.0}
abby sobh
fuente
-1

O simplemente mydict.keys()eso es un método integrado para la creación de diccionarios. También explorar mydict.values()y mydict.items().

// Ah, la publicación de OP me confundió.

Edgar Aroutiounian
fuente
55
Los métodos integrados son útiles, pero no dan una lista de elementos correspondientes de una lista de claves dada. Esta respuesta no es una respuesta correcta a esta pregunta en particular.
stenix
-1

Después del cierre de Python: forma eficiente de crear una lista a partir de valores dict con un orden dado

Recuperando las claves sin construir la lista:

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import collections


class DictListProxy(collections.Sequence):
    def __init__(self, klist, kdict, *args, **kwargs):
        super(DictListProxy, self).__init__(*args, **kwargs)
        self.klist = klist
        self.kdict = kdict

    def __len__(self):
        return len(self.klist)

    def __getitem__(self, key):
        return self.kdict[self.klist[key]]


myDict = {'age': 'value1', 'size': 'value2', 'weigth': 'value3'}
order_list = ['age', 'weigth', 'size']

dlp = DictListProxy(order_list, myDict)

print(','.join(dlp))
print()
print(dlp[1])

La salida:

value1,value3,value2

value3

Que coincide con el orden dado por la lista

mementum
fuente
-2
reduce(lambda x,y: mydict.get(y) and x.append(mydict[y]) or x, mykeys,[])

en caso de que no haya llaves en el dict.

yupbank
fuente