Dizionario Python: ottieni un elenco di valori per l'elenco di chiavi


182

Esiste un modo integrato / rapido per utilizzare un elenco di chiavi di un dizionario per ottenere un elenco di elementi corrispondenti?

Ad esempio ho:

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

Come posso usare mykeysper ottenere i valori corrispondenti nel dizionario come elenco?

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

Risposte:


206

Una comprensione dell'elenco sembra essere un buon modo per farlo:

>>> [mydict[x] for x in mykeys]
[3, 1]

1
Se mydictè una chiamata di funzione (che restituisce un dict), questa chiama la funzione più volte, giusto?
endolith

1
@endolith Sì, lo farà
Eric Romrell,

108

Un paio di modi diversi da list-comp:

  • Costruisci elenco e genera un'eccezione se la chiave non viene trovata: map(mydict.__getitem__, mykeys)
  • Elenco di build con Nonese la chiave non è stata trovata:map(mydict.get, mykeys)

In alternativa, l'utilizzo operator.itemgetterpuò restituire una tupla:

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

Nota : in Python3, maprestituisce un iteratore anziché un elenco. Utilizzare list(map(...))per un elenco.


54

Un piccolo confronto di velocità:

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

Quindi la comprensione dell'elenco e il itemgetter sono i modi più veloci per farlo.

AGGIORNAMENTO: Per grandi elenchi casuali e mappe ho avuto risultati leggermente diversi:

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

Quindi in questo caso il chiaro vincitore è f = operator.itemgetter(*l); f(m), e l'outsider chiaro: map(lambda _: m[_], l).

AGGIORNAMENTO per 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)

Quindi, i risultati per Python 3.6.4 sono quasi gli stessi.


15

Ecco tre modi.

Sollevare KeyErrorquando la chiave non viene trovata:

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

Valori predefiniti per chiavi mancanti.

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

Saltare le chiavi mancanti.

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

found_keys = mapping.keys() & iterableTypeError: unsupported operand type(s) for &: 'list' and 'list'su Python 2.7; `found_keys = [chiave per chiave in mapping.keys () se chiave in iterabile] funziona meglio
NotGaeL

10

Prova questo:

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]

7

Prova questo:

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]

@PeterDeGlopper sei confuso. items()è preferito, non è necessario effettuare una ricerca aggiuntiva, qui non c'è nessuna len(mydict)*len(mykeys)operazione! (nota che sto usando un set)
Óscar López,

@ ÓscarLópez Sì, stai controllando ogni elemento del dizionario. iteritems non li produce fino a quando non ne hai bisogno, quindi evita di costruire un elenco intermedio, ma esegui comunque 'k in mykeys' (ordina len (mykeys), poiché è un elenco) per ogni k in mydict. Completamente inutile, rispetto alla più semplice comprensione dell'elenco che scorre solo su mykeys.
Peter DeGlopper,

@ inspectorG4dget @PeterDeGlopper l'operazione di iscrizione mykeysè ammortizzata a tempo costante, sto usando un set, non un elenco
Óscar López,

2
La conversione dell'elenco dei PO in un set almeno lo rende lineare, ma è comunque lineare sulla struttura di dati errata e perde l'ordine. Considera il caso di un dizionario 10k e 2 chiavi in ​​mykeys. La tua soluzione effettua 10k test di appartenenza al set, rispetto a due ricerche nel dizionario per la semplice comprensione dell'elenco. In generale, sembra sicuro supporre che il numero di chiavi sarà inferiore al numero di elementi del dizionario e, in caso contrario, il tuo approccio ometterà elementi ripetuti.
Peter DeGlopper,


1

Panda lo fa in modo molto elegante, anche se spesso la comprensione dell'elenco sarà sempre tecnicamente più pitonica. Non ho tempo di fare un confronto della velocità in questo momento (tornerò più tardi e lo inserirò):

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}

-1

O semplicemente mydict.keys()Questo è un metodo incorporato per i dizionari. Anche esplorare mydict.values()emydict.items() .

// Ah, post OP mi ha confuso.


5
I metodi integrati sono utili ma non forniscono un elenco di elementi corrispondenti da un determinato elenco di chiavi. Questa risposta non è una risposta corretta a questa particolare domanda.
stenix,

-1

Dopo la chiusura di Python: modo efficiente per creare un elenco da valori di dict con un determinato ordine

Recupero delle chiavi senza creare l'elenco:

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

Il risultato:

value1,value3,value2

value3

Che corrisponde all'ordine indicato dalla lista


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

in caso ci siano chiavi non in dict.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.