Перетворення двох списків у словник


1227

Уявіть, що у вас є:

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

Який найпростіший спосіб скласти наступний словник?

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

Відповіді:


2141

Подобається це:

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

Voila :-) 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

139

Уявіть, що у вас є:

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, використовуючи середні значення, розуміння дикту дійсно все ж швидше, приблизно на 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,019microsec за dict(zip(headList, textList))& 1,95 \ pm 0,030 microsec for {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, використовуючи середні значення, розуміння дикту дійсно все ж швидше, приблизно на 30% для цих невеликих списків. Для більш великих списків (10 к випадкових чисел) dictдзвінок приблизно на 10% швидше.
naught101

@ naught101 - я відповів на ваші коментарі у своїй відповіді.
Аарон Холл

3
Числа в 10 тис. Були просто швидким способом створення 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'}

8

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

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

Це можна зробити за допомогою одного рядка коду:

d = {List1[n]: List2[n] for n in range(len(List1))}

6
провалюється голосно, якщо List1довшеList2
Жан-Франсуа Фабре

@ Жан-ФрансуаFabre Чи це дуже важливо? що є причиною того, що нам слід скласти два списки різної довжини для складання словника?
loved.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 а не просте присвоєння пари ключ-значення. Зрештою, більшість випадків фактор часу є важливим криголам. Тому я провів операцію часу для обох підходів.

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

0

Рішення як розуміння словника з перерахуванням:

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

Рішення щодо циклу з перерахуванням:

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

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

метод без функції 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'}

Привіт xiyurui, Вхідні дані (l1 та l2) повинні бути списком. Якщо ви призначите l1 та l2 як набір, він може не зберігати порядок вставки. для мене я отримав вихід як {1: 'a', 2: 'c', 3: 'd', 4: 'b', 5: 'e'}
Nursnaaz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.