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


182

Чи є вбудований / швидкий спосіб використовувати список ключів до словника, щоб отримати список відповідних елементів?

Наприклад, у мене є:

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

Як я можу mykeysотримати відповідні значення у словнику як список?

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

Відповіді:


206

Зрозуміння списку здається хорошим способом зробити це:

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

1
Якщо mydictце виклик функції (який повертає диктат), то він викликає функцію кілька разів, правда?
ендоліт

1
@endolith Так, це буде
Ерік Ромрелл

108

Кілька інших способів, ніж список-comp:

  • Створіть список та викиньте виняток, якщо ключ не знайдено: map(mydict.__getitem__, mykeys)
  • Створіть список, Noneякщо ключ не знайдено: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

Тож розуміння списків і перебір предметів - це найшвидший спосіб зробити це.

ОНОВЛЕННЯ: Для великих випадкових списків і карт я мав дещо інші результати:

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 майже однакові.


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]

found_keys = mapping.keys() & iterableдає TypeError: unsupported operand type(s) for &: 'list' and 'list'на python 2.7; `found_keys = [ключ для ключа в mapping.keys (), якщо ключ в ітерабелі] працює найкраще
NotGaeL


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)операції! (зауважте, що я використовую набір)
Óscar López

@ ÓscarLópez Так, ви переглядаєте кожен елемент словника. iteritems не поступається їм, поки вам не знадобляться, тому він уникає створення посередницького списку, але ви все одно запускаєте 'k in mykeys' (замовлення len (mykeys), оскільки це список) для кожного k у моєму винесенні. Зовсім без потреби, порівняно з більш простим розумінням списку, яке просто перебігає мійків.
Пітер ДеГлоппер

@ inspectorG4dget @PeterDeGlopper Операція членства закінчується mykeysпостійним часом, я використовую набір, а не список
carscar Лопес

2
Перетворення списку ОП у набір принаймні робить його лінійним, але він все ще лінійний за неправильною структурою даних, а також втратою порядку. Розглянемо випадок словника 10k та 2 клавіш у мійках. У вашому рішенні робиться 10-ти набір тестів на членство, порівняно з двома пошуковими пошуковими словниками для простого розуміння списку. Загалом, здається, можна припустити, що кількість клавіш буде меншою за кількість елементів словника - і якщо це не так, ваш підхід опустить повторювані елементи.
Пітер ДеГлоппер


1

Pandas робить це дуже елегантно, хоча розуміння списків завжди буде більш технічно пітонічним. Я не встигаю зараз порівнювати швидкість (я повернусь пізніше і вкладу):

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
Вбудовані методи корисні, але вони не дають переліку відповідних елементів із заданого списку клавіш. Ця відповідь не є правильною відповіддю на це конкретне питання.
stenix

-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

Що відповідає порядку, заданому списком


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

на випадок, якщо є ключі не в дікт.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.