Dicionário Python: obtenha uma lista de valores para a lista de chaves

182

Existe uma maneira integrada / rápida de usar uma lista de chaves de um dicionário para obter uma lista dos itens correspondentes?

Por exemplo, eu tenho:

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

Como posso usar mykeyspara obter os valores correspondentes no dicionário como uma lista?

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

Respostas:

206

Uma compreensão da lista parece ser uma boa maneira de fazer isso:

>>> [mydict[x] for x in mykeys]
[3, 1]
FazJaxton
fonte
1
Se mydicté uma chamada de função (que retorna um ditado), isso chama a função várias vezes, certo?
endolith
1
@endolith Sim, será #
Eric Romrell # 9/19
108

Algumas outras maneiras além da lista-comp:

  • Crie a lista e ative a exceção se a chave não for encontrada: map(mydict.__getitem__, mykeys)
  • Crie uma lista com a Nonechave if não encontrada:map(mydict.get, mykeys)

Como alternativa, use operator.itemgetterpode retornar uma tupla:

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

Nota : no Python3, mapretorna um iterador em vez de uma lista. Use list(map(...))para uma lista.

Jon Clements
fonte
54

Uma pequena comparação de velocidade:

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

Portanto, a compreensão da lista e o itemgetter são as maneiras mais rápidas de fazer isso.

ATUALIZAÇÃO: Para listas e mapas aleatórios grandes, obtive resultados um pouco 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

Portanto, neste caso o vencedor claro é f = operator.itemgetter(*l); f(m), e fora claro: map(lambda _: m[_], l).

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

Portanto, os resultados para o Python 3.6.4 são quase os mesmos.

Sklavit
fonte
15

Aqui estão três maneiras.

Aumentando KeyErrorquando a chave não foi encontrada:

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

Valores padrão para chaves ausentes.

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

Ignorando as chaves ausentes.

result = [mapping[k] for k in iterable if k in mapping]
OdraEncoded
fonte
found_keys = mapping.keys() & iterableTypeError: unsupported operand type(s) for &: 'list' and 'list'no python 2.7; `found_keys = [chave para chave em mapping.keys () se chave em iterável] funciona melhor
NotGaeL
10

Tente isto:

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
fonte
7

Tente o seguinte:

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
fonte
@PeterDeGlopper você está confuso. items()é preferível, não precisa fazer uma pesquisa adicional, não há len(mydict)*len(mykeys)operação aqui! (note que eu estou usando um conjunto)
Óscar López
@ ÓscarLópez Sim, você está inspecionando todos os elementos do dicionário. iteritems não os produz até que você precise deles, portanto evita a construção de uma lista intermediária, mas você ainda executa 'k in mykeys' (encomende len (mykeys), já que é uma lista) para cada k no mydict. Completamente desnecessário, comparado com a compreensão mais simples da lista que simplesmente atropela o mykeys.
Peter DeGlopper
@ inspectorG4dget @PeterDeGlopper a operação de associação sobre mykeysa hora constante amortizado, estou usando um conjunto, não uma lista
Óscar López
2
Converter a lista de OP em um conjunto pelo menos a torna linear, mas ainda é linear na estrutura de dados incorreta, além de perder a ordem. Considere o caso de um dicionário de 10k e 2 teclas em mykeys. Sua solução faz testes de associação de 10 mil conjuntos, em comparação com duas pesquisas de dicionário para facilitar a compreensão da lista. Em geral, parece seguro assumir que o número de chaves será menor que o número de elementos do dicionário - e, se não for, sua abordagem omitirá elementos repetidos.
Peter DeGlopper
4
new_dict = {x: v for x, v in mydict.items() if x in mykeys}
Pavel Minenkov
fonte
1

Pandas faz isso de maneira muito elegante, embora a compreensão de muitas listas sempre seja mais tecnicamente pitônica. Não tenho tempo para fazer uma comparação de velocidade agora (voltarei mais tarde e colocarei):

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
fonte
-1

Ou apenas mydict.keys()Isso é uma chamada de método interna para dicionários. Também explore mydict.values()e mydict.items().

// Ah, OP post me confundiu.

Edgar Aroutiounian
fonte
5
Os métodos internos são úteis, mas não fornecem uma lista dos itens correspondentes de uma determinada lista de chaves. Esta resposta não é uma resposta correta para esta pergunta em particular.
stenix
-1

Após o fechamento do Python: maneira eficiente de criar uma lista de valores dict com uma determinada ordem

Recuperando as chaves sem criar a 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])

A saída:

value1,value3,value2

value3

Que corresponde à ordem dada pela lista

lembrança
fonte
-2
reduce(lambda x,y: mydict.get(y) and x.append(mydict[y]) or x, mykeys,[])

caso haja chaves que não estão no ditado.

yupbank
fonte