Как преобразовать два списка в словарь?

Представьте, что у вас есть следующий список.

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

Как проще всего составить следующий словарь?

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

person Guido    schedule 16.10.2008    source источник


Ответы (16)


Нравится:

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

Вуаля :-) Попарный конструктор dict и _ 3_ чрезвычайно полезна.

person Dan Lenski    schedule 16.10.2008
comment
Стоит отметить, что dictionary = {zip(keys, values)} не сработает. Вы должны явно указать как dict(...) - person Fernando Wittmann; 28.08.2019
comment
Не уверен, почему вы этого ожидали, @FernandoWittmann. {thing} - это синтаксический сахар для создания set(), содержащего один элемент. {*iterable} - это синтаксический сахар для создания set, содержащего несколько элементов. {k:v} или {**mapping} будет построить dict, но это синтаксически совершенно различно. - person Dan Lenski; 28.08.2019
comment
Спасибо за комментарий, Дэн. Ты прав. Мое замешательство произошло из-за того, что я обычно использую синтаксис {} для словарей. Фактически, если мы попробуем type({}), результат будет dict. Но действительно, если мы попробуем type({thing}), то на выходе будет set. - person Fernando Wittmann; 28.08.2019
comment
Я пришел сюда на случай, если мы сможем добиться большего, чем {k:v for k, v in zip(keys, values)}. Оказывается, можем. +1. - person J.G.; 23.01.2020
comment
@FernandoWittmann, вы правы, что это сбивает с толку. {[thing1, thing2, … thingN]} создает набор для любого значения N != 0; но для N == 0 он создает пустой dict, и вам нужно сделать set(), чтобы создать пустой набор. Это несколько прискорбно и PoLS нарушает Python из-за того, что Python dict-литералы были задолго до set-литералов. - person Dan Lenski; 29.10.2020
comment
Целью хранения кортежа zip-списка в качестве словаря является удаление повторяющегося аспекта zip. zip возвращает итерируемые кортежи. - person Golden Lion; 05.11.2020

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

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 (а не понимания list, как ошибочно выразились другие):

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

izip из 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>

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

Менее эффективные альтернативы:

Это выражение генератора передается конструктору 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% для этих небольших списков. Для больших списков (10k случайных чисел) вызов 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
person Aaron Hall    schedule 16.11.2015
comment
По состоянию на середину 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. - person Mark_Anderson; 02.07.2019
comment
Кажется, вы говорите, что понимание dict происходит быстрее всего, но тогда в обзоре производительности dict(zip(keys, values)) выглядит быстрее. Может ты что то забыл обновить? - person Loisaida Sam Sandberg; 08.04.2020
comment
Незначительное примечание (в значительной степени не относящееся к Python 2 EOL): вы можете использовать from future_builtins import zip в качестве альтернативы from itertools import izip as zip, что немного более четко описывает импорт с точки зрения получения Python 3 zip в качестве замены обычного zip. Это в точности эквивалентно тому, чтобы быть ясным (future_builtins.zip сам по себе просто псевдоним itertools.izip). - person ShadowRanger; 29.10.2020

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

>>> 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.

person Mike Davis    schedule 16.10.2008
comment
Верно для Python2, но в Python 3 zip уже экономно расходует память. docs.python.org/3/library/functions.html#zip Фактически, вы можете видеть, что six использует zip в Python 3 для замены itertools.izip в Python 2 pythonhosted.org/six. - person Pedro Cattori; 17.02.2017

Вы также можете использовать словарные выражения в 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'}
person Brendan Berg    schedule 10.06.2012

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

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')    
dict = {keys[i]: values[i] for i in range(len(keys))}
person Polla A. Fattah    schedule 16.11.2015
comment
иногда это самый быстрый способ, а иногда самый медленный, чтобы преобразовать в объект dict, почему это так? Спасибо, чувак. - person Haritsinh Gohil; 08.08.2019

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

>>> adict = dict((str(k), v) for k, v in zip(['a', 1, 'b'], [2, 'c', 3])) 

Взгляните на Код как Pythonista: Идиоматический Python.

person jfs    schedule 16.10.2008

с Python 3.x подходит для понимания dict

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'}
person kiriloff    schedule 25.05.2013

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

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

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

d = {List1[n]: List2[n] for n in range(len(List1))}
person exploitprotocol    schedule 29.03.2013
comment
громко не работает, если List1 длиннее List2 - person Jean-François Fabre; 13.09.2017
comment
@ Jean-FrançoisFabre Это действительно важно? по какой причине мы должны создать два списка разной длины, чтобы построить словарь? - person loved.by.Jesus; 18.12.2019
comment
вероятно, нет, но после этого for n in range(len(List1)) - анти-паттерн - person Jean-François Fabre; 18.12.2019

  • 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')
person AbstProcDo    schedule 18.04.2018

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

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

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

person Akash Nayak    schedule 16.11.2017

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

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]}
person Cyd    schedule 20.02.2019

У меня было это сомнение, когда я пытался решить проблему, связанную с графом. Проблема, с которой я столкнулся, заключалась в том, что мне нужно было определить пустой список смежности и я хотел инициализировать все узлы с пустым списком, тогда я подумал, как насчет того, чтобы проверить, достаточно ли он быстр, я имею в виду, стоит ли выполнять операцию 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_-м шаге.

person Mayank Prakash    schedule 20.07.2019

Решение как понимание словаря с enumerate:

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

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

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

Если вы работаете с более чем одним набором значений и хотите иметь список словаря, вы можете использовать это:

def as_dict_list(data: list, columns: list):
    return [dict((zip(columns, row))) for row in data]

Пример из реальной жизни - это список кортежей из запроса к базе данных в паре с кортежем столбцов из того же запроса. Остальные ответы предоставлены только 1 к 1.

person DonkeyKong    schedule 16.12.2020

метод без функции zip

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'}
person xiyurui    schedule 24.05.2017
comment
Привет, xiyurui, вход (l1 и l2) должен быть списком. Если вы назначите l1 и l2 как набор, он может не сохранить порядок вставки. для меня я получил результат как {1: 'a', 2: 'c', 3: 'd', 4: 'b', 5: 'e'} - person Nursnaaz; 31.01.2019

Хотя есть несколько способов сделать это, но я считаю наиболее фундаментальным подходом к этому; создание цикла и словаря и сохранение значений в этом словаре. В рекурсивном подходе идея все та же, но вместо использования цикла функция вызывала себя, пока не дойдет до конца. Конечно, есть и другие подходы, такие как использование dict(zip(key, value)) и т. Д. Это не самые эффективные решения.

y = [1,2,3,4]
x = ["a","b","c","d"]

# This below is a brute force method
obj = {}
for i in range(len(y)):
    obj[y[i]] = x[i]
print(obj)

# Recursive approach 
obj = {}
def map_two_lists(a,b,j=0):
    if j < len(a):
        obj[b[j]] = a[j]
        j +=1
        map_two_lists(a, b, j)
        return obj
      


res = map_two_lists(x,y)
print(res)

Оба результата должны напечатать

{1: 'a', 2: 'b', 3: 'c', 4: 'd'}  
person Franco    schedule 28.08.2020