Конвертировать два списка в словарь

1229

Представь, что у тебя есть:

keys = ['name', 'age', 'food']
values = ['Monty', 42, 'spam']

Какой самый простой способ создать следующий словарь?

a_dict = {'name' : 'Monty', 'age' : 42, 'food' : 'spam'}
Guido
источник

Ответы:

2145

Нравится:

>>> keys = ['a', 'b', 'c']
>>> values = [1, 2, 3]
>>> dictionary = dict(zip(keys, values))
>>> print(dictionary)
{'a': 1, 'b': 2, 'c': 3}

Вуаля :-) Парный dictконструктор и zipфункция очень полезны: https://docs.python.org/3/library/functions.html#func-dict

Дэн Ленски
источник
3
Стоит отметить, что dictionary = {zip(keys, values)}это не сработает. Вы должны явно объявить какdict(...)
Фернандо Виттманн
5
Не знаю, почему вы этого ожидаете, @FernandoWittmann. {thing}является синтаксическим сахаром для построения set()содержащего один элемент. {*iterable}является синтаксическим сахаром для построения, setсодержащего несколько элементов. {k:v}или {**mapping} будет построить dict, но это синтаксический совершенно различно.
Дэн Ленски
6
Спасибо за комментарий, Дэн. Вы правы. Моя путаница произошла, потому что я обычно использую синтаксис {}для словарей. На самом деле, если мы попробуем, type({})результат будет dict. Но действительно, если мы попробуем, type({thing})то результат будет set.
Фернандо Виттманн
Я пришел сюда на случай, если мы сможем сделать лучше, чем {k:v for k, v in zip(keys, values)}. Оказывается, мы можем. +1.
JG
140

Представь, что у тебя есть:

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')

Какой самый простой способ создать следующий словарь?

dict = {'name' : 'Monty', 'age' : 42, 'food' : 'spam'}

Самый производительный, dictконструктор сzip

new_dict = dict(zip(keys, values))

В Python 3 zip теперь возвращает ленивый итератор, и теперь это наиболее эффективный подход.

dict(zip(keys, values))требуется одноразовый глобальный поиск для каждого dictи zip, но он не образует ненужных промежуточных структур данных и не должен иметь дело с локальными поисками в приложении функции.

Второе место, понимание слова:

Близкое второе место к использованию конструктора dict состоит в том, чтобы использовать собственный синтаксис понимания dict (не понимания списка , как ошибочно выразились другие):

new_dict = {k: v for k, v in zip(keys, values)}

Выберите это, когда вам нужно отобразить или отфильтровать на основе ключей или значения.

В Python 2 zipвозвращает список, чтобы избежать создания ненужного списка, используйте izipвместо этого (псевдоним zip может уменьшить изменения кода при переходе на Python 3).

from itertools import izip as zip

Итак, это все еще (2.7):

new_dict = {k: v for k, v in zip(keys, values)}

Python 2, идеально подходит для <= 2.6

izipfrom itertoolsстановится zipв Python 3. izipлучше, чем zip для Python 2 (потому что это избегает ненужного создания списка), и идеально подходит для 2.6 или ниже:

from itertools import izip
new_dict = dict(izip(keys, values))

Результат для всех случаев:

В любом случае:

>>> new_dict
{'age': 42, 'name': 'Monty', 'food': 'spam'}

Объяснение:

Если мы посмотрим на справку, dictто увидим, что она принимает различные формы аргументов:


>>> help(dict)

class dict(object)
 |  dict() -> new empty dictionary
 |  dict(mapping) -> new dictionary initialized from a mapping object's
 |      (key, value) pairs
 |  dict(iterable) -> new dictionary initialized as if via:
 |      d = {}
 |      for k, v in iterable:
 |          d[k] = v
 |  dict(**kwargs) -> new dictionary initialized with the name=value pairs
 |      in the keyword argument list.  For example:  dict(one=1, two=2)

Оптимальным подходом является использование итерации, избегая при этом создания ненужных структур данных. В Python 2 zip создает ненужный список:

>>> zip(keys, values)
[('name', 'Monty'), ('age', 42), ('food', 'spam')]

В Python 3 эквивалент будет:

>>> list(zip(keys, values))
[('name', 'Monty'), ('age', 42), ('food', 'spam')]

и Python 3 zipпросто создает повторяемый объект:

>>> zip(keys, values)
<zip object at 0x7f0e2ad029c8>

Поскольку мы хотим избежать создания ненужных структур данных, мы обычно хотим избегать Python 2 zip(поскольку он создает ненужный список).

Менее производительные альтернативы:

Это выражение генератора передается конструктору dict:

generator_expression = ((k, v) for k, v in zip(keys, values))
dict(generator_expression)

или эквивалентно:

dict((k, v) for k, v in zip(keys, values))

И это понимание списка, передаваемое конструктору dict:

dict([(k, v) for k, v in zip(keys, values)])

В первых двух случаях дополнительный слой неоперативных (таким образом, ненужных) вычислений помещается поверх итерируемого zip, а в случае понимания списка дополнительный список создается без необходимости. Я ожидал бы, что все они будут менее производительными, и конечно не более.

Обзор производительности:

В 64-битном Python 3.8.2, предоставленном Nix, в Ubuntu 16.04 упорядочен от самого быстрого до самого медленного:

>>> min(timeit.repeat(lambda: dict(zip(keys, values))))
0.6695233230129816
>>> min(timeit.repeat(lambda: {k: v for k, v in zip(keys, values)}))
0.6941362579818815
>>> min(timeit.repeat(lambda: {keys[i]: values[i] for i in range(len(keys))}))
0.8782548159942962
>>> 
>>> min(timeit.repeat(lambda: dict([(k, v) for k, v in zip(keys, values)])))
1.077607496001292
>>> min(timeit.repeat(lambda: dict((k, v) for k, v in zip(keys, values))))
1.1840861019445583

dict(zip(keys, values)) выигрывает даже с небольшими наборами ключей и значений, но для больших наборов различия в производительности станут больше.

Комментатор сказал:

minкажется плохой способ сравнить производительность. Наверняка meanи / или maxбыли бы гораздо более полезные показатели для реального использования.

Мы используем, minпотому что эти алгоритмы являются детерминированными. Мы хотим знать производительность алгоритмов в наилучших возможных условиях.

Если операционная система по какой-либо причине зависает, это не имеет ничего общего с тем, что мы пытаемся сравнить, поэтому мы должны исключить такие результаты из нашего анализа.

Если бы мы использовали mean, то такого рода события сильно исказили бы наши результаты, и если бы мы использовали, maxмы получим только самый экстремальный результат - тот, который наиболее вероятно затронут таким событием.

Комментатор также говорит:

В Python 3.6.8, используя средние значения, понимание dict действительно еще быстрее, примерно на 30% для этих небольших списков. Для больших списков (10 тыс. Случайных чисел) dictвызов происходит примерно на 10% быстрее.

Я предполагаю, что мы имеем в виду dict(zip(...10k случайных чисел. Это звучит как довольно необычный вариант использования. Имеет смысл, что самые прямые вызовы будут доминировать в больших наборах данных, и я не удивлюсь, если зависания ОС будут доминирующими, учитывая, сколько времени потребуется для запуска этого теста, что еще больше искажает ваши цифры. И если вы используете meanили maxя бы посчитал ваши результаты бессмысленными.

Давайте использовать более реалистичный размер в наших лучших примерах:

import numpy
import timeit
l1 = list(numpy.random.random(100))
l2 = list(numpy.random.random(100))

И мы видим здесь, что dict(zip(...действительно работает быстрее для больших наборов данных примерно на 20%.

>>> min(timeit.repeat(lambda: {k: v for k, v in zip(l1, l2)}))
9.698965263989521
>>> min(timeit.repeat(lambda: dict(zip(l1, l2))))
7.9965161079890095
Аарон Холл
источник
1
По состоянию на середину 2019 года (Python 3.7.3), я нахожу разные сроки. %% timeit возвращает 1,57 \ pm 0,019 мкс для dict(zip(headList, textList))& 1,95 \ pm 0,030 мкс для {k: v for k, v in zip(headList, textList)}. Я бы предложил первый для удобочитаемости и скорости. Очевидно, это получает аргумент min () vs mean () для timeit.
Mark_Anderson
1
minкажется плохой способ сравнить производительность. Наверняка meanи / или maxбыли бы гораздо более полезные показатели для реального использования.
naught101
1
В Python 3.6.8, используя средние значения, понимание dict действительно еще быстрее, примерно на 30% для этих небольших списков. Для больших списков (10 тыс. Случайных чисел) dictвызов происходит примерно на 10% быстрее.
naught101
@ naught101 - я ответил на ваши комментарии в своем ответе.
Аарон Холл
3
Числа 10k были просто быстрым способом создать 2 длинных списка уникальных элементов. Генерация списка была сделана вне временных оценок. // Почему вы думаете, что значит или Макс бесполезны? Если вы делаете это много раз, то ваше среднее время равно ~ n * среднее, а верхнее ограничено ~ n * max. Ваш минимум обеспечивает нижнюю границу, но большинство людей заботятся о средней или худшей производительности. Если есть большая разница, ваш минимум будет совершенно не представлен в большинстве случаев. Как минимум является более значимым в сценарии реального мира?
naught101
128

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

>>> import itertools
>>> keys = ('name', 'age', 'food')
>>> values = ('Monty', 42, 'spam')
>>> adict = dict(itertools.izip(keys,values))
>>> adict
{'food': 'spam', 'age': 42, 'name': 'Monty'}

В Python 2 это также более экономно по сравнению с потреблением памяти zip.

Майк Дэвис
источник
18
Правда для Python2, но в Python 3 zipуже экономно расходует память. docs.python.org/3/library/functions.html#zip На самом деле вы можете видеть, что sixиспользуется zipв Python 3 для замены itertools.izipв Python 2 pythonhosted.org/six .
Педро
35
>>> keys = ('name', 'age', 'food')
>>> values = ('Monty', 42, 'spam')
>>> dict(zip(keys, values))
{'food': 'spam', 'age': 42, 'name': 'Monty'}
ины
источник
28

Вы также можете использовать словарь в Python ≥ 2.7:

>>> keys = ('name', 'age', 'food')
>>> values = ('Monty', 42, 'spam')
>>> {k: v for k, v in zip(keys, values)}
{'food': 'spam', 'age': 42, 'name': 'Monty'}
Брендан Берг
источник
17

Более естественным способом является использование словарного понимания

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')    
dict = {keys[i]: values[i] for i in range(len(keys))}
Полла А. Фаттах
источник
иногда это самый быстрый способ, а иногда медленнее всего конвертировать в dictобъект, почему так? Спасибо, чувак.
Харицин Гохил
10

с Python 3.x, для понимания

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')

dic = {k:v for k,v in zip(keys, values)}

print(dic)

Больше на понимании вины, здесь есть пример:

>>> print {i : chr(65+i) for i in range(4)}
    {0 : 'A', 1 : 'B', 2 : 'C', 3 : 'D'}
kiriloff
источник
8

Для тех, кто нуждается в простом коде и не знаком с zip:

List1 = ['This', 'is', 'a', 'list']
List2 = ['Put', 'this', 'into', 'dictionary']

Это можно сделать одной строкой кода:

d = {List1[n]: List2[n] for n in range(len(List1))}
exploitprotocol
источник
6
громко терпит неудачу, если List1дольше, чемList2
Жан-Франсуа Фабр
@ Жан-Франсуа Фабр: Это действительно важно? по какой причине мы должны создать два списка разной длины, чтобы создать словарь?
love.by.Иисус
вероятно нет, но после этого for n in range(len(List1))происходит анти-паттерн
Жан-Франсуа Фабр
3
  • 2018-04-18

Лучшее решение по-прежнему:

In [92]: keys = ('name', 'age', 'food')
...: values = ('Monty', 42, 'spam')
...: 

In [93]: dt = dict(zip(keys, values))
In [94]: dt
Out[94]: {'age': 42, 'food': 'spam', 'name': 'Monty'}

Транспонировать это:

    lst = [('name', 'Monty'), ('age', 42), ('food', 'spam')]
    keys, values = zip(*lst)
    In [101]: keys
    Out[101]: ('name', 'age', 'food')
    In [102]: values
    Out[102]: ('Monty', 42, 'spam')
Исчисление
источник
2

Вы можете использовать этот код ниже:

dict(zip(['name', 'age', 'food'], ['Monty', 42, 'spam']))

Но убедитесь, что длина списков будет одинаковой. Если длина не одинакова. Затем функция zip перевернет длинную.

Акаш Наяк
источник
2

У меня было это сомнение, когда я пытался решить проблему, связанную с графами. Проблема, с которой я столкнулся, заключалась в том, что мне нужно было определить пустой список смежности, и я хотел инициализировать все узлы пустым списком, и тогда я подумал, как насчет того, чтобы проверить, достаточно ли это быстро, то есть стоит ли выполнять операцию zip вместо простой пары ключ-значение присваивания. Ведь в большинстве случаев фактор времени является важным ледоколом. Поэтому я выполнил операцию timeit для обоих подходов.

import timeit
def dictionary_creation(n_nodes):
    dummy_dict = dict()
    for node in range(n_nodes):
        dummy_dict[node] = []
    return dummy_dict


def dictionary_creation_1(n_nodes):
    keys = list(range(n_nodes))
    values = [[] for i in range(n_nodes)]
    graph = dict(zip(keys, values))
    return graph


def wrapper(func, *args, **kwargs):
    def wrapped():
        return func(*args, **kwargs)
    return wrapped

iteration = wrapper(dictionary_creation, n_nodes)
shorthand = wrapper(dictionary_creation_1, n_nodes)

for trail in range(1, 8):
    print(f'Itertion: {timeit.timeit(iteration, number=trails)}\nShorthand: {timeit.timeit(shorthand, number=trails)}')

Для n_nodes = 10 000 000 я получаю,

Итерация: 2.825081646999024 Сокращение: 3.535717916001886

Итерация: 5.051560923002398 Сокращение: 6.255070794999483

Итерация: 6.52859034499852 Сокращение: 8.221581164998497

Итерация: 8.683652416999394 Сокращение: 12.599181543999293

Итерация: 11.587241565001023 Сокращение: 15.27298851100204

Итерация: 14.816342867001367 Сокращение: 17.162912737003353

Итерация: 16.645022411001264 Сокращение: 19.976680120998935

После определенного момента вы можете ясно видеть, что итерационный подход на n-м шаге обгоняет время, необходимое для стенографического подхода на n-1-м шаге.

Маянк Пракаш
источник
1

Вот также пример добавления значения списка в вашем словаре

list1 = ["Name", "Surname", "Age"]
list2 = [["Cyd", "JEDD", "JESS"], ["DEY", "AUDIJE", "PONGARON"], [21, 32, 47]]
dic = dict(zip(list1, list2))
print(dic)

всегда проверяйте, чтобы ваш «Ключ» (list1) всегда был в первом параметре.

{'Name': ['Cyd', 'JEDD', 'JESS'], 'Surname': ['DEY', 'AUDIJE', 'PONGARON'], 'Age': [21, 32, 47]}
Cyd
источник
0

Решение как словарь с перечислением:

dict = {item : values[index] for index, item in enumerate(keys)}

Решение для цикла с перечислением:

dict = {}
for index, item in enumerate(keys):
    dict[item] = values[index]
jay123
источник
0

Вы также можете попробовать с одним списком, который является комбинацией двух списков;)

a = [1,2,3,4]
n = [5,6,7,8]

x = []
for i in a,n:
    x.append(i)

print(dict(zip(x[0], x[1])))
Лахан Рамават
источник
-1

метод без функции почтового индекса

l1 = [1,2,3,4,5]
l2 = ['a','b','c','d','e']
d1 = {}
for l1_ in l1:
    for l2_ in l2:
        d1[l1_] = l2_
        l2.remove(l2_)
        break  

print (d1)


{1: 'd', 2: 'b', 3: 'e', 4: 'a', 5: 'c'}
xiyurui
источник
Привет xiyurui, вход (l1 и l2) должен быть список. Если вы назначите l1 и l2 как набор, это может не сохранить порядок вставки. для меня я получил вывод: {1: 'a', 2: 'c', 3: 'd', 4: 'b', 5: 'e'}
Nursnaaz