Словарь Python: получить список значений для списка ключей

182

Существует ли встроенный / быстрый способ использовать список ключей словаря для получения списка соответствующих элементов?

Например, у меня есть:

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

Как я могу использовать, mykeysчтобы получить соответствующие значения в словаре в виде списка?

>>> mydict.WHAT_GOES_HERE(mykeys)
[3, 1]
FazJaxton
источник

Ответы:

206

Понимание списка, кажется, хороший способ сделать это:

>>> [mydict[x] for x in mykeys]
[3, 1]
FazJaxton
источник
1
Если mydictэто вызов функции (который возвращает dict), то это вызывает функцию несколько раз, верно?
Эндолит
1
@endolith Да, это будет
Эрик Ромрелл
108

Пара других способов, чем list-comp:

  • Составьте список и сгенерируйте исключение, если ключ не найден: map(mydict.__getitem__, mykeys)
  • Составить список с Noneключом if not found:map(mydict.get, mykeys)

Кроме того, использование operator.itemgetterможет вернуть кортеж:

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

Примечание : в Python3 mapвозвращает итератор, а не список. Используйте list(map(...))для списка.

Джон Клементс
источник
54

Небольшое сравнение скорости:

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

Таким образом, понимание списка и itemgetter - самые быстрые способы сделать это.

ОБНОВЛЕНИЕ: Для больших случайных списков и карт у меня были немного другие результаты:

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

Таким образом , в этом случае ясно победитель f = operator.itemgetter(*l); f(m), и аутсайдер: map(lambda _: m[_], l).

ОБНОВЛЕНИЕ для 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)

Итак, результаты для Python 3.6.4 практически одинаковы.

Sklavit
источник
15

Вот три способа.

Поднятие, KeyErrorкогда ключ не найден:

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

Значения по умолчанию для отсутствующих ключей.

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

Пропуск пропущенных ключей.

result = [mapping[k] for k in iterable if k in mapping]
OdraEncoded
источник
found_keys = mapping.keys() & iterableдает TypeError: unsupported operand type(s) for &: 'list' and 'list'на питоне 2.7; `found_keys = [ключ для ключа в mapping.keys (), если ключ в итерируемом] работает лучше всего
NotGaeL
10

Попробуй это:

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

Попробуй это:

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 вы в замешательстве. items()является предпочтительным, он не должен делать дополнительный поиск, здесь нет len(mydict)*len(mykeys)операции! (обратите внимание, что я использую набор)
Оскар Лопес
@ ÓscarLópez Да, вы проверяете каждый элемент словаря. iteritems не выдает их до тех пор, пока они вам не понадобятся, поэтому он избегает создания промежуточного списка, но вы все равно запускаете 'k в mykeys' (порядок len (mykeys), так как это список) для каждого k в mydict. Совершенно излишне, по сравнению с более простым пониманием списков, которое работает только над mykeys.
Питер ДеГлоппер
@ inspectorG4dget @PeterDeGlopper операция членства в течение mykeysамортизированного постоянного времени, я использую набор, а не список
Оскар Лопес
2
Преобразование списка OP в набор, по крайней мере, делает его линейным, но он по-прежнему линейен для неправильной структуры данных, а также для убыточного порядка. Рассмотрим случай словаря из 10 тысяч слов и двух ключей в mykeys. Ваше решение выполняет 10 000 тестов на членство по сравнению с двумя поисками в словаре для простого понимания списка. В целом кажется безопасным предположить, что количество ключей будет меньше, чем количество элементов словаря - и если это не так, ваш подход пропустит повторяющиеся элементы.
Питер ДеГлоппер
4
new_dict = {x: v for x, v in mydict.items() if x in mykeys}
Павел Миненков
источник
1

Панды делают это очень элегантно, хотя понимание списка часто будет более технически Pythonic. Сейчас у меня нет времени на сравнение скорости (я вернусь позже и добавлю):

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

Или просто mydict.keys()вызов встроенного метода для словарей. Также исследуйте mydict.values()и mydict.items().

// Ах, ОП сообщение смутило меня.

Эдгар Арутюнян
источник
5
Встроенные методы полезны, но они не дают список соответствующих элементов из данного списка ключей. Этот ответ не является правильным ответом на этот конкретный вопрос.
Стеникс
-1

После закрытия Python: эффективный способ создать список из значений dict с заданным порядком

Получение ключей без создания списка:

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

Выход:

value1,value3,value2

value3

Что соответствует порядку, указанному в списке

mementum
источник
-2
reduce(lambda x,y: mydict.get(y) and x.append(mydict[y]) or x, mykeys,[])

В случае, если есть ключи не в dict.

yupbank
источник