Converti due elenchi in un dizionario


1229

Immagina di avere:

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

Qual è il modo più semplice per produrre il seguente dizionario?

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

Risposte:


2145

Come questo:

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

Voila :-) Il dictcostruttore e la zipfunzione a coppie sono incredibilmente utili: https://docs.python.org/3/library/functions.html#func-dict


3
Vale la pena notare che dictionary = {zip(keys, values)}non funzionerà. Devi dichiarare esplicitamente comedict(...)
Fernando Wittmann il

5
Non so perché te lo aspetti, @FernandoWittmann. {thing}è zucchero sintattico per costruire un set()contenente un elemento. {*iterable}è zucchero sintattico per costruire un setcontenente più elementi. {k:v}o {**mapping} sarà costruire una dict, ma è sintatticamente ben distinti.
Dan Lenski,

6
Grazie per il commento Dan. Hai ragione. La mia confusione è avvenuta perché di solito uso la sintassi {}per i dizionari. In effetti, se proviamo type({})l'output è dict. Ma in effetti, se ci proviamo, type({thing})l'output è set.
Fernando Wittmann,

Sono venuto qui nel caso in cui possiamo fare di meglio {k:v for k, v in zip(keys, values)}. Si scopre che possiamo. +1.
JG

140

Immagina di avere:

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

Qual è il modo più semplice per produrre il seguente dizionario?

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

Più performante, dictcostruttore conzip

new_dict = dict(zip(keys, values))

In Python 3, zip ora restituisce un iteratore pigro, e questo è ora l'approccio più performante.

dict(zip(keys, values))richiede la ricerca globale una tantum per ciascuna dicte zip, ma non costituisce alcuna struttura di dati intermedia non necessaria né deve occuparsi di ricerche locali nell'applicazione delle funzioni.

Secondo classificato, comprensione del dict:

Un secondo passo per usare il costruttore dict è usare la sintassi nativa di una comprensione dict (non una comprensione dell'elenco , come altri l'hanno erroneamente definita):

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

Scegli questo quando è necessario mappare o filtrare in base alle chiavi o al valore.

In Python 2, ziprestituisce un elenco, per evitare di creare un elenco non necessario, utilizzare izipinvece (aliasato per zip può ridurre le modifiche al codice quando si passa a Python 3).

from itertools import izip as zip

Quindi è ancora (2.7):

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

Python 2, ideale per <= 2.6

izipfrom itertoolsdiventa zipin Python 3. izipè meglio di zip per Python 2 (perché evita la creazione di elenchi superflui) e ideale per 2.6 o precedenti:

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

Risultato per tutti i casi:

In tutti i casi:

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

Spiegazione:

Se guardiamo l'aiuto su dictvediamo che ci vogliono una varietà di forme di argomenti:


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

L'approccio ottimale è utilizzare un iterabile evitando di creare strutture di dati non necessarie. In Python 2, zip crea un elenco non necessario:

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

In Python 3, l'equivalente sarebbe:

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

e Python 3 zipcrea semplicemente un oggetto iterabile:

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

Poiché vogliamo evitare di creare strutture di dati non necessarie, di solito vogliamo evitare Python 2 zip(poiché crea un elenco non necessario).

Alternative meno performanti:

Questa è un'espressione del generatore che viene passata al costruttore dict:

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

o equivalentemente:

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

E questa è una comprensione dell'elenco che viene passata al costruttore del dict:

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

Nei primi due casi, un ulteriore livello di calcolo non operativo (quindi non necessario) viene posizionato sopra lo zip iterabile e, nel caso della comprensione dell'elenco, viene creato inutilmente un elenco aggiuntivo. Mi aspetterei che fossero tutti meno performanti, e certamente non di più.

Valutazione delle prestazioni:

In 64 bit Python 3.8.2 fornito da Nix, su Ubuntu 16.04, ordinato dal più veloce al più lento:

>>> 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)) vince anche con piccoli set di chiavi e valori, ma per set più grandi, le differenze nelle prestazioni diventeranno maggiori.

Un commentatore ha detto:

minsembra un brutto modo per confrontare le prestazioni. Sicuramente meane / o maxsarebbero indicatori molto più utili per un utilizzo reale.

Usiamo minperché questi algoritmi sono deterministici. Vogliamo conoscere le prestazioni degli algoritmi nelle migliori condizioni possibili.

Se il sistema operativo si blocca per qualsiasi motivo, non ha nulla a che fare con ciò che stiamo cercando di confrontare, quindi dobbiamo escludere questo tipo di risultati dalla nostra analisi.

Se lo usassimo mean, questo tipo di eventi distorcerebbe notevolmente i nostri risultati, e se lo usassimo maxotterremo solo il risultato più estremo - quello che più probabilmente risentirà di un tale evento.

Un commentatore dice anche:

In python 3.6.8, usando i valori medi, la comprensione del dict è davvero ancora più veloce, di circa il 30% per questi piccoli elenchi. Per elenchi più grandi (10k numeri casuali), la dictchiamata è circa il 10% più veloce.

Presumo che intendiamo dict(zip(...con 10k numeri casuali. Sembra un caso d'uso piuttosto insolito. Ha senso che le chiamate più dirette dominino in set di dati di grandi dimensioni, e non sarei sorpreso se gli hang del sistema operativo stanno dominando dato il tempo necessario per eseguire quel test, distorcendo ulteriormente i numeri. E se usi meano maxconsidererei i tuoi risultati insignificanti.

Usiamo una dimensione più realistica nei nostri migliori esempi:

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

E vediamo qui che dict(zip(...effettivamente funziona più velocemente per set di dati più grandi di circa il 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
A partire dalla metà del 2019 (python 3.7.3), trovo diversi tempi. %% timeit restituisce 1.57 \ pm 0.019microsec per dict(zip(headList, textList))& 1.95 \ pm 0.030 microsec per {k: v for k, v in zip(headList, textList)}. Vorrei suggerire il primo per leggibilità e velocità. Ovviamente questo arriva all'argomento min () vs mean () per timeit.
Mark_Anderson,

1
minsembra un brutto modo per confrontare le prestazioni. Sicuramente meane / o maxsarebbero indicatori molto più utili per un utilizzo reale.
naught101,

1
In python 3.6.8, usando i valori medi, la comprensione del dict è davvero ancora più veloce, di circa il 30% per questi piccoli elenchi. Per elenchi più grandi (10k numeri casuali), la dictchiamata è circa il 10% più veloce.
naught101,

@ naught101 - Ho risposto ai tuoi commenti nella mia risposta.
Aaron Hall

3
I numeri da 10k erano solo un modo rapido per generare 2 lunghi elenchi di elementi unici. La generazione dell'elenco è stata effettuata al di fuori delle stime dei tempi. / / Perché pensi che media o massima siano inutili? Se lo fai molte volte, allora il tuo tempo medio è ~ n * medio e limite superiore di ~ n * max. Il tuo minimo fornisce un limite inferiore, ma la maggior parte delle persone si preoccupa delle prestazioni medie o peggiori. Se c'è una varianza elevata, il minimo sarà del tutto non rappresentativo nella maggior parte dei casi. In che modo il minimo è più significativo in uno scenario del mondo reale?
naught101

128

Prova questo:

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

In Python 2, è anche più economico nel consumo di memoria rispetto a zip.


18
Vero per Python2, ma in Python 3, zipè già economico nel consumo di memoria. docs.python.org/3/library/functions.html#zip In effetti, puoi vedere che sixutilizza zipin Python 3 per sostituire itertools.izipin 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

Puoi anche usare la comprensione del dizionario in 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

Un modo più naturale è usare la comprensione del dizionario

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

a volte è il modo più veloce e a volte è più lento convertire in dictoggetto, perché è così? Grazie amico.
Haritsinh Gohil,


10

con Python 3.x, vale per la comprensione del dict

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

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

print(dic)

Maggiori informazioni sulla comprensione dei dettami qui , c'è un esempio:

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

8

Per coloro che hanno bisogno di un codice semplice e non hanno familiarità con zip:

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

Questo può essere fatto con una riga di codice:

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

6
fallisce rumorosamente se List1è più lungo diList2
Jean-François Fabre

@ Jean-FrançoisFabre Importa davvero? qual è il motivo per cui dovremmo creare due elenchi di diversa lunghezza per costruire un dizionario?
amato da Gesù, il

probabilmente no, ma dopo questo for n in range(len(List1))è un anti-pattern
Jean-François Fabre

3
  • 2018/04/18

La soluzione migliore è ancora:

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

Traslocare:

    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

puoi usare questo codice qui sotto:

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

Ma assicurati che la lunghezza degli elenchi sia la stessa. Se la lunghezza non è la stessa, allora la funzione zip trasforma quella più lunga.


2

Ho avuto questo dubbio mentre cercavo di risolvere un problema legato al grafico. Il problema che avevo era che dovevo definire un elenco di adiacenza vuoto e volevo inizializzare tutti i nodi con un elenco vuoto, fu allora che pensai a come controllare se è abbastanza veloce, intendo se varrà la pena fare un'operazione zip anziché una semplice coppia chiave-valore di assegnazione. Dopo tutto il più delle volte, il fattore tempo è un importante rompighiaccio. Quindi ho eseguito un'operazione timeit per entrambi gli approcci.

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

Per n_nodes = 10.000.000 che ottengo,

Iterazione: 2.825081646999024 Stenografia: 3.535717916001886

Iterazione: 5.051560923002398 Stenografia: 6.255070794999483

Iterazione: 6.52859034499852 Stenografia: 8.221581164998497

Iterazione: 8.683652416999394 Stenografia: 12.599181543999293

Iterazione: 11.587241565001023 Stenografia: 15.27298851100204

Iterazione: 14.816342867001367 Stenografia: 17.162912737003353

Iterazione: 16.645022411001264 Stenografia: 19.976680120998935

È possibile vedere chiaramente dopo un certo punto che l'approccio di iterazione all'n-step supera il tempo impiegato dall'approccio di stenografia all'n-1-step.


1

Ecco anche un esempio di aggiunta di un valore di elenco nel dizionario

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

assicurati sempre che la tua "Chiave" (list1) sia sempre nel primo parametro.

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

0

Soluzione come comprensione del dizionario con enumerazione:

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

Soluzione come per loop con enumerazione:

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

0

Puoi anche provare con un elenco che è una combinazione di due elenchi;)

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

metodo senza funzione 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'}

Ciao xiyurui, l'input (l1 e l2) dovrebbe essere un elenco. Se si assegnano l1 e l2 come set, è possibile che l'ordine di inserimento non venga conservato. per me ho ottenuto l'output come {1: 'a', 2: 'c', 3: 'd', 4: 'b', 5: 'e'}
Nursnaaz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.