Słownik Pythona: pobierz listę wartości dla listy kluczy

182

Czy istnieje wbudowany / szybki sposób użycia listy kluczy do słownika w celu uzyskania listy odpowiednich pozycji?

Na przykład mam:

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

Jak mogę użyć, mykeysaby uzyskać odpowiednie wartości w słowniku jako listę?

>>> mydict.WHAT_GOES_HERE(mykeys)
[3, 1]
FazJaxton
źródło

Odpowiedzi:

206

Rozumienie listy wydaje się być dobrym sposobem na zrobienie tego:

>>> [mydict[x] for x in mykeys]
[3, 1]
FazJaxton
źródło
1
Jeśli mydictjest wywołaniem funkcji (która zwraca dict), to wywołuje funkcję wiele razy, prawda?
endolith
1
@endolith Yes it will
Eric Romrell
108

Kilka innych sposobów niż list-comp:

  • Zbuduj listę i zgłoś wyjątek, jeśli klucz nie zostanie znaleziony: map(mydict.__getitem__, mykeys)
  • Utwórz listę z Nonekluczem if not found:map(mydict.get, mykeys)

Alternatywnie użycie operator.itemgettermoże zwrócić krotkę:

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

Uwaga : w Pythonie3 mapzwraca iterator zamiast listy. Użyj list(map(...))dla listy.

Jon Clements
źródło
54

Małe porównanie prędkości:

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

Zrozumienie listy i tworzenie elementów to najszybsze sposoby na zrobienie tego.

AKTUALIZACJA: W przypadku dużych losowych list i map miałem nieco inne wyniki:

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

Więc w tym przypadku oczywiste jest zwycięzcą f = operator.itemgetter(*l); f(m), a jasne outsider: map(lambda _: m[_], l).

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

Zatem wyniki dla Pythona 3.6.4 są prawie takie same.

Sklavit
źródło
15

Oto trzy sposoby.

Podnoszenie, KeyErrorgdy nie znaleziono klucza:

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

Domyślne wartości brakujących kluczy.

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

Pomijanie brakujących kluczy.

result = [mapping[k] for k in iterable if k in mapping]
OdraEncoded
źródło
found_keys = mapping.keys() & iterabledaje TypeError: unsupported operand type(s) for &: 'list' and 'list'na Pythonie 2.7; `found_keys = [klucz do klucza w mapping.keys () jeśli klucz jest iterowalny] działa najlepiej
NotGaeL
10

Spróbuj tego:

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]
Żyrandol Vikram Singh
źródło
7

Spróbuj tego:

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
źródło
@PeterDeGlopper jesteś zdezorientowany. items()jest preferowana, nie musi wykonywać dodatkowego wyszukiwania, nie ma len(mydict)*len(mykeys)tu żadnej operacji! (zauważ, że używam zestawu)
Óscar López
@ ÓscarLópez Tak, sprawdzasz każdy element słownika. iteritems nie daje ich, dopóki ich nie potrzebujesz, więc unika tworzenia listy pośredniczącej, ale nadal uruchamiasz 'k in mykeys' (kolejność len (mykeys), ponieważ jest to lista) dla każdego k w moim słowniku. Zupełnie niepotrzebnie, w porównaniu z prostszym zrozumieniem list, które po prostu działa na mykeys.
Peter DeGlopper
@ inspectorG4dget @PeterDeGlopper operacja członkostwa mykeysjest amortyzowana przez cały czas, używam zestawu, a nie listy
Óscar López
2
Konwersja listy PO do zestawu przynajmniej czyni ją liniową, ale nadal jest liniowa w przypadku niewłaściwej struktury danych, a także utraty kolejności. Rozważmy przypadek słownika 10k i 2 kluczy w mykeys. Twoje rozwiązanie umożliwia wykonanie 10 000 testów członkostwa w zestawach, w porównaniu z dwoma przeszukiwaniami słowników w celu prostego zrozumienia listy. Generalnie wydaje się bezpieczne założenie, że liczba kluczy będzie mniejsza niż liczba elementów słownika - a jeśli tak nie jest, twoje podejście pominie powtarzające się elementy.
Peter DeGlopper
4
new_dict = {x: v for x, v in mydict.items() if x in mykeys}
Pavel Minenkov
źródło
1

Pandy robią to bardzo elegancko, chociaż często składane listy zawsze będą bardziej technicznie Pythonowe. Nie mam teraz czasu na porównanie prędkości (wrócę później i wstawię):

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
źródło
-1

Lub po prostu mydict.keys()To jest wbudowane wywołanie metody dla słowników. Przeglądaj także mydict.values()i mydict.items().

// Ah, post OP mnie zdezorientował.

Edgar Aroutiounian
źródło
5
Wbudowane metody są przydatne, ale nie dają listy odpowiadających im pozycji z podanej listy kluczy. Ta odpowiedź nie jest poprawną odpowiedzią na to konkretne pytanie.
stenix
-1

Po zamknięciu Pythona: skuteczny sposób tworzenia listy z wartości dyktowania w zadanej kolejności

Pobieranie kluczy bez budowania listy:

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

Wyjście:

value1,value3,value2

value3

Który pasuje do kolejności podanej na liście

mementum
źródło
-2
reduce(lambda x,y: mydict.get(y) and x.append(mydict[y]) or x, mykeys,[])

o ile nie ma kluczy w dyktandzie.

yupbank
źródło