Konwertuj dwie listy na słownik


1226

Wyobraź sobie, że masz:

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

Jaki jest najprostszy sposób na utworzenie następującego słownika?

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

Odpowiedzi:


2140

Lubię to:

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

Voila :-) dictKonstruktor i zipfunkcja parujące są niezwykle przydatne: https://docs.python.org/3/library/functions.html#func-dict


3
Warto zauważyć, że dictionary = {zip(keys, values)}to nie zadziała. Musisz jawnie zadeklarować jakodict(...)
Fernando Wittmann

5
Nie jestem pewien, dlaczego się tego spodziewałeś, @FernandoWittmann. {thing}jest cukrem syntaktycznym do skonstruowania set()zawierającego jeden element. {*iterable}jest cukrem syntaktycznym do skonstruowania setzawierającego kilka elementów. {k:v}lub {**mapping} będzie skonstruować dict, ale to składniowo zupełnie odrębne.
Dan Lenski

6
Dzięki za komentarz, Dan. Masz rację. Moje zamieszanie zdarzyło się, ponieważ zwykle używam sintax {}do słowników. W rzeczywistości, jeśli spróbujemy, type({})wynik będzie dict. Ale rzeczywiście, jeśli spróbujemy, type({thing})wynik będzie set.
Fernando Wittmann

Przybyłem tu na wypadek, gdybyśmy mogli zrobić lepiej niż {k:v for k, v in zip(keys, values)}. Okazuje się, że możemy. +1.
JG

139

Wyobraź sobie, że masz:

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

Jaki jest najprostszy sposób na utworzenie następującego słownika?

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

Najbardziej wydajny dictkonstruktorzip

new_dict = dict(zip(keys, values))

W Pythonie 3 zip zwraca teraz leniwy iterator i jest to obecnie najbardziej wydajne podejście.

dict(zip(keys, values))wymaga każdorazowego jednorazowego globalnego wyszukiwania dicti zip, ale nie tworzy niepotrzebnych pośrednich struktur danych ani nie musi zajmować się lokalnymi przeglądami w aplikacji funkcji.

Drugie miejsce, zrozumienie dykta:

Drugim miejscem do korzystania z konstruktora dict jest użycie natywnej składni rozumienia dict (nie rozumienia listy , jak inni błędnie to powiedzieli):

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

Wybierz tę opcję, gdy chcesz mapować lub filtrować na podstawie kluczy lub wartości.

W Pythonie 2 zipzwraca listę, aby uniknąć tworzenia niepotrzebnej listy, użyj izipzamiast tego (aliasy do zip mogą zmniejszyć zmiany kodu po przejściu do Pythona 3).

from itertools import izip as zip

Tak więc nadal (2.7):

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

Python 2, idealny dla <= 2.6

izipfrom itertoolsstaje się zipw Pythonie 3. izipjest lepszy niż zip dla Pythona 2 (ponieważ pozwala uniknąć niepotrzebnego tworzenia listy) i idealny dla wersji 2.6 lub niższej:

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

Wynik dla wszystkich przypadków:

We wszystkich przypadkach:

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

Wyjaśnienie:

Jeśli spojrzymy na pomoc dict, widzimy, że wymaga ona różnych form argumentów:


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

Optymalnym podejściem jest stosowanie iterowalności, przy jednoczesnym unikaniu tworzenia niepotrzebnych struktur danych. W Pythonie 2 zip tworzy niepotrzebną listę:

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

W Pythonie 3 odpowiednikiem byłoby:

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

a Python 3 ziptworzy jedynie iterowalny obiekt:

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

Ponieważ chcemy uniknąć tworzenia niepotrzebnych struktur danych, zwykle chcemy uniknąć Pythona 2 zip(ponieważ tworzy niepotrzebną listę).

Mniej wydajne alternatywy:

To wyrażenie generatora jest przekazywane do konstruktora dict:

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

lub równoważnie:

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

Oto koncept listy przekazywany konstruktorowi dykt:

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

W pierwszych dwóch przypadkach dodatkowa warstwa nieoperacyjnego (a więc niepotrzebnego) obliczenia jest umieszczana nad iterowalnym zamkiem błyskawicznym, aw przypadku zrozumienia listy niepotrzebnie tworzona jest dodatkowa lista. Spodziewałbym się, że wszystkie będą mniej wydajne, a na pewno nie bardziej.

Przegląd wydajności:

W 64-bitowym Pythonie 3.8.2 dostarczonym przez Nix na Ubuntu 16.04, uporządkowanym od najszybszego do najwolniejszego:

>>> 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)) wygrywa nawet przy małych zestawach kluczy i wartości, ale w przypadku większych zestawów różnice w wydajności będą większe.

Komentator powiedział:

minwydaje się zły sposób na porównanie wydajności. Z pewnością meani / lub maxbyłyby o wiele bardziej przydatne wskaźniki do rzeczywistego wykorzystania.

Używamy, minponieważ te algorytmy są deterministyczne. Chcemy poznać wydajność algorytmów w najlepszych możliwych warunkach.

Jeśli system operacyjny zawiesza się z jakiegokolwiek powodu, nie ma to nic wspólnego z tym, co próbujemy porównać, dlatego musimy wykluczyć tego rodzaju wyniki z naszej analizy.

Gdybyśmy zastosowali mean, tego rodzaju zdarzenia znacznie zniekształcałyby nasze wyniki, a gdybyśmy zastosowali, maxotrzymalibyśmy tylko najbardziej ekstremalny wynik - ten, na który najprawdopodobniej wpłynie takie zdarzenie.

Komentator mówi również:

W Pythonie 3.6.8, przy użyciu wartości średnich, zrozumienie dykta jest rzeczywiście jeszcze szybsze, o około 30% dla tych małych list. W przypadku większych list (10 000 losowych numerów) dictpołączenie jest około 10% szybsze.

Zakładam, że mamy na myśli dict(zip(...10k liczb losowych. To brzmi jak dość nietypowy przypadek użycia. To ma sens, że najbardziej bezpośrednie połączenia dominują w dużych zestawach danych i nie zdziwiłbym się, gdyby zawieszenia systemu dominowały, biorąc pod uwagę, ile czasu zajęłoby uruchomienie tego testu, dodatkowo wypaczając liczby. A jeśli użyjesz meanlub maxuznałbym twoje wyniki za bezsensowne.

Zastosujmy bardziej realistyczny rozmiar w naszych najlepszych przykładach:

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

Widzimy tutaj, że dict(zip(...rzeczywiście działa szybciej dla większych zestawów danych o około 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
Od połowy 2019 r. (Python 3.7.3) znajduję różne czasy. %% timeit zwraca 1,57 \ pm 0,019 mikrosekundy dla dict(zip(headList, textList))i 1,95 \ pm 0,030 mikrosekundy dla {k: v for k, v in zip(headList, textList)}. Sugerowałbym ten pierwszy ze względu na czytelność i szybkość. Oczywiście jest to argument argumentu min () vs mean () dla timeit.
Mark_Anderson

1
minwydaje się zły sposób na porównanie wydajności. Z pewnością meani / lub maxbyłyby o wiele bardziej przydatne wskaźniki do rzeczywistego wykorzystania.
naught101

1
W Pythonie 3.6.8, przy użyciu wartości średnich, zrozumienie dykta jest rzeczywiście jeszcze szybsze, o około 30% dla tych małych list. W przypadku większych list (10 000 losowych numerów) dictpołączenie jest około 10% szybsze.
naught101

@ naught101 - Odpowiedziałem na twoje komentarze w mojej odpowiedzi.
Aaron Hall

3
Liczby 10 tys. To tylko szybki sposób na wygenerowanie 2 długiej listy unikalnych elementów. Generowania listy dokonano poza szacunkami czasowymi. / / Dlaczego uważasz, że średnie lub maksymalne są bezużyteczne? Jeśli robisz to wiele razy, to twój średni czas wynosi ~ n * średni, a górny jest ograniczony przez ~ n * max. Twoje minimum zapewnia dolną granicę, ale większość ludzi dba o średnią lub najgorszą wydajność. Jeśli występuje duża wariancja, Twoje minimum będzie całkowicie niereprezentatywne w większości przypadków. W jaki sposób minimum ma większe znaczenie w realnym świecie?
naught101

128

Spróbuj tego:

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

W Pythonie 2 zużycie pamięci jest również bardziej ekonomiczne w porównaniu do zip.


18
To prawda dla Python2, ale w Python 3 zipjest już oszczędny w zużyciu pamięci. docs.python.org/3/library/functions.html#zip W rzeczywistości widać, że sixużywa zipPython 3 do zastępowania itertools.izipw Python 2 pythonhosted.org/six .
Pedro Cattori

35
>>> keys = ('name', 'age', 'food')
>>> values = ('Monty', 42, 'spam')
>>> dict(zip(keys, values))
{'food': 'spam', 'age': 42, 'name': 'Monty'}

28

W języku Python ≥ 2.7 możesz także używać wyrażeń słownikowych:

>>> keys = ('name', 'age', 'food')
>>> values = ('Monty', 42, 'spam')
>>> {k: v for k, v in zip(keys, values)}
{'food': 'spam', 'age': 42, 'name': 'Monty'}

17

Bardziej naturalnym sposobem jest użycie słownika

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')    
dict = {keys[i]: values[i] for i in range(len(keys))}

czasami jest to najszybszy sposób, a czasem najwolniej konwersja na dictobiekt, dlaczego tak jest ?, dzięki, stary.
Haritsinh Gohil


10

w Pythonie 3.x chodzi o rozumienie nagrań

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

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

print(dic)

Więcej na temat rozumienia nagrań tutaj , przykład:

>>> print {i : chr(65+i) for i in range(4)}
    {0 : 'A', 1 : 'B', 2 : 'C', 3 : 'D'}

8

Dla tych, którzy potrzebują prostego kodu i nie są zaznajomieni z zip:

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

Można to zrobić za pomocą jednego wiersza kodu:

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

6
zawodzi głośno, jeśli List1jest dłuższy niżList2
Jean-François Fabre

@ Jean-FrançoisFabre Czy to naprawdę ma znaczenie? z jakiego powodu powinniśmy stworzyć dwie listy o różnej długości, aby zbudować słownik?
loved.by.Jesus,

prawdopodobnie nie, ale po tym for n in range(len(List1))jest anty-wzór
Jean-François Fabre

3
  • 2018-04-18

Najlepszym rozwiązaniem jest nadal:

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

Przepisz to:

    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

możesz użyć tego kodu poniżej:

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

Ale upewnij się, że długość list będzie taka sama. Jeśli długość nie jest taka sama. Następnie funkcja zip obróć dłuższą.


2

Miałem wątpliwości, gdy próbowałem rozwiązać problem związany z grafem. Problemem było to, że musiałem zdefiniować pustą listę przylegania i chciałem zainicjować wszystkie węzły z pustą listą, wtedy pomyślałem o tym, czy sprawdzę, czy jest wystarczająco szybki, to znaczy, czy warto wykonać operację zip zamiast prostej pary klucz-wartość przypisania. W większości przypadków czynnik czasu jest ważnym czynnikiem łamającym lód. Więc wykonałem operację timeit dla obu podejść.

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)}')

Za n_nodes = 10 000 000 dostaję,

Iteracja: 2,825081646999024 Stenografia: 3,535717916001886

Iteracja: 5.051560923002398 Stenografia: 6,255070794999483

Iteracja: 6,52859034499852 Stenografia: 8,221581164998497

Iteracja: 8.683652416999394 Stenografia: 12.599181543999293

Iteracja: 11.587241565001023 Stenografia: 15,27298851100204

Iteracja: 14,816342867001367 Stenografia: 17.162912737003353

Iteracja: 16,645022411001264 Skrót: 19,976680120998935

Po pewnym punkcie widać wyraźnie, że podejście iteracyjne na n-tym kroku wyprzedza czas potrzebny na podejście skrótowe na n-1 kroku.


1

Oto także przykład dodania wartości listy do słownika

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

zawsze upewnij się, że Twój „klucz” (lista1) jest zawsze w pierwszym parametrze.

{'Name': ['Cyd', 'JEDD', 'JESS'], 'Surname': ['DEY', 'AUDIJE', 'PONGARON'], 'Age': [21, 32, 47]}

0

Rozwiązanie jako słownikowe z wyliczeniem:

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

Rozwiązanie jak dla pętli z wyliczeniem:

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

0

Możesz także spróbować z jedną listą, która jest kombinacją dwóch list;)

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

metoda bez funkcji 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'}

Cześć xiyurui, Wejście (l1 i l2) powinno być listą. Jeśli przypiszesz l1 i l2 jako zestaw, może nie zachować kolejności wstawiania. dla mnie mam wynik jako {1: 'a', 2: 'c', 3: 'd', 4: 'b', 5: 'e'}
Nursnaaz
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.