Convertir deux listes en un dictionnaire

1229

Imaginez que vous ayez:

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

Quelle est la manière la plus simple de produire le dictionnaire suivant?

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

Réponses:

2145

Comme ça:

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

Voila :-) Le dictconstructeur et la zipfonction par paire sont extrêmement utiles: https://docs.python.org/3/library/functions.html#func-dict

Dan Lenski
la source
3
Il convient de noter que dictionary = {zip(keys, values)}cela ne fonctionnera pas. Vous devez déclarer explicitement quedict(...)
Fernando Wittmann
5
Je ne sais pas pourquoi vous vous y attendez, @FernandoWittmann. {thing}est du sucre syntaxique pour construire un set()contenant un élément. {*iterable}est du sucre syntaxique pour construire un setcontenant plusieurs éléments. {k:v}ou {**mapping} va construire un dict, mais c'est syntaxiquement assez distinct.
Dan Lenski
6
Merci pour le commentaire Dan. Tu as raison. Ma confusion s'est produite parce que j'utilise habituellement la sintax {}pour les dictionnaires. En fait, si nous essayons, type({})la sortie est dict. Mais en effet, si nous essayons, type({thing})la sortie est set.
Fernando Wittmann
Je suis venu ici au cas où nous pourrions faire mieux que {k:v for k, v in zip(keys, values)}. Il s'avère que nous le pouvons. +1.
JG
140

Imaginez que vous ayez:

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

Quelle est la manière la plus simple de produire le dictionnaire suivant?

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

dictConstructeur le plus performant aveczip

new_dict = dict(zip(keys, values))

En Python 3, zip renvoie maintenant un itérateur paresseux, et c'est maintenant l'approche la plus performante.

dict(zip(keys, values)) ne nécessite la recherche globale unique pour dict et zip, mais il ne forme pas de structures de données intermédiaires inutiles ou n'a pas à traiter les recherches locales dans l'application de fonction.

Finaliste, compréhension du dict:

Un finaliste proche de l'utilisation du constructeur de dict est d'utiliser la syntaxe native d'une compréhension de dict (pas une compréhension de liste , comme d'autres l'ont dit à tort):

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

Choisissez cette option lorsque vous devez mapper ou filtrer en fonction des clés ou de la valeur.

En Python 2, zip renvoie une liste, pour éviter de créer une liste inutile, utilisez izipplutôt (l'alias de zip peut réduire les modifications de code lorsque vous passez à Python 3).

from itertools import izip as zip

Donc ça reste (2.7):

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

Python 2, idéal pour <= 2.6

izipfrom itertoolsdevient zipen Python 3. izipest meilleur que zip pour Python 2 (car il évite la création de liste inutile), et idéal pour 2.6 ou inférieur:

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

Résultat pour tous les cas:

Dans tous les cas:

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

Explication:

Si nous regardons l'aide, dictnous voyons qu'elle prend une variété d'arguments:


>>> 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'approche optimale consiste à utiliser un itérable tout en évitant de créer des structures de données inutiles. En Python 2, zip crée une liste inutile:

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

En Python 3, l'équivalent serait:

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

et Python 3 zipcrée simplement un objet itérable:

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

Puisque nous voulons éviter de créer des structures de données inutiles, nous voulons généralement éviter celles de Python 2 zip(car cela crée une liste inutile).

Alternatives moins performantes:

Il s'agit d'une expression de générateur transmise au constructeur dict:

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

ou équivalent:

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

Et ceci est une compréhension de liste transmise au constructeur de dict:

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

Dans les deux premiers cas, une couche supplémentaire de calcul non opérationnel (donc inutile) est placée sur le zip itérable, et dans le cas de la compréhension de liste, une liste supplémentaire est créée inutilement. Je m'attendrais à ce qu'ils soient tous moins performants, et certainement pas plus.

Revue de la performance:

En Python 3.8.2 64 bits fourni par Nix, sur Ubuntu 16.04, ordonné du plus rapide au plus lent:

>>> 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)) gagne même avec de petits ensembles de clés et de valeurs, mais pour des ensembles plus grands, les différences de performances deviendront plus importantes.

Un intervenant a déclaré:

minsemble être une mauvaise façon de comparer les performances. Sûrement meanet / ou maxseraient des indicateurs beaucoup plus utiles pour une utilisation réelle.

Nous utilisons min parce que ces algorithmes sont déterministes. Nous voulons connaître les performances des algorithmes dans les meilleures conditions possibles.

Si le système d'exploitation se bloque pour une raison quelconque, cela n'a rien à voir avec ce que nous essayons de comparer, nous devons donc exclure ce type de résultats de notre analyse.

Si nous l'utilisions mean, ce genre d'événements fausserait considérablement nos résultats, et si nous l'utilisions, maxnous n'obtiendrions que le résultat le plus extrême - celui le plus probablement affecté par un tel événement.

Un commentateur dit également:

En python 3.6.8, en utilisant des valeurs moyennes, la compréhension du dict est en effet encore plus rapide, d'environ 30% pour ces petites listes. Pour les listes plus grandes (10 000 nombres aléatoires), l' dictappel est environ 10% plus rapide.

Je suppose que nous voulons dire dict(zip(...avec 10 000 nombres aléatoires. Cela ressemble à un cas d'utilisation assez inhabituel. Il est logique que les appels les plus directs dominent dans les grands ensembles de données, et je ne serais pas surpris que les blocages du système d'exploitation dominent étant donné le temps qu'il faudrait pour exécuter ce test, faussant davantage vos chiffres. Et si vous utilisez meanoumax je considérerais vos résultats comme dénués de sens.

Utilisons une taille plus réaliste sur nos meilleurs exemples:

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

Et nous voyons ici que dict(zip(...cela fonctionne en effet plus rapidement pour les grands ensembles de données d'environ 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
Aaron Hall
la source
1
À partir de la mi-2019 (python 3.7.3), je trouve différents timings. %% timeit renvoie 1,57 \ pm 0,019 microsec pour dict(zip(headList, textList))& 1,95 \ pm 0,030 microsec pour {k: v for k, v in zip(headList, textList)}. Je suggère le premier pour la lisibilité et la vitesse. Évidemment, cela arrive à l'argument min () vs mean () pour timeit.
Mark_Anderson
1
minsemble être une mauvaise façon de comparer les performances. Sûrement meanet / ou maxseraient des indicateurs beaucoup plus utiles pour une utilisation réelle.
naught101
1
En python 3.6.8, en utilisant des valeurs moyennes, la compréhension du dict est en effet encore plus rapide, d'environ 30% pour ces petites listes. Pour les listes plus grandes (10 000 nombres aléatoires), l' dictappel est environ 10% plus rapide.
naught101
@ naught101 - J'ai adressé vos commentaires dans ma réponse.
Aaron Hall
3
Les nombres 10k n'étaient qu'un moyen rapide de générer 2 longue liste d'éléments uniques. La génération de la liste a été effectuée en dehors des estimations temporelles. / / Pourquoi pensez-vous que le moyen ou le maximum sont inutiles? Si vous faites cela plusieurs fois, votre temps moyen est de ~ n * moyenne, et supérieur de ~ n * max. Votre minimum fournit une limite inférieure, mais la plupart des gens se soucient des performances moyennes ou du pire des cas. S'il y a un écart élevé, votre minimum sera totalement non représentatif de la plupart des cas. Comment le minimum est-il plus significatif dans un scénario réel?
naught101
128

Essaye ça:

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

En Python 2, il est également plus économique en termes de consommation de mémoire par rapport à zip.

Mike Davis
la source
18
Vrai pour Python2, mais en Python 3, il zipest déjà économique en consommation de mémoire. docs.python.org/3/library/functions.html#zip En fait, vous pouvez voir que sixutilise zipdans Python 3 pour remplacer itertools.izipdans 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'}
iny
la source
28

Vous pouvez également utiliser des compréhensions de dictionnaire en 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'}
Brendan Berg
la source
17

Une façon plus naturelle consiste à utiliser la compréhension du dictionnaire

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')    
dict = {keys[i]: values[i] for i in range(len(keys))}
Polla A. Fattah
la source
parfois c'est le moyen le plus rapide et parfois c'est plus lent à convertir en dictobjet, pourquoi est-ce ainsi?, merci mec.
Haritsinh Gohil
10

avec Python 3.x, opte pour les compréhensions dict

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

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

print(dic)

Plus d'informations sur les compréhensions dict ici , un exemple est là:

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

Pour ceux qui ont besoin de code simple et ne connaissent pas zip:

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

Cela peut être fait par une seule ligne de code:

d = {List1[n]: List2[n] for n in range(len(List1))}
exploiter le protocole
la source
6
échoue bruyamment si List1est plus long queList2
Jean-François Fabre
@ Jean-FrançoisFabre Est-ce vraiment important? quelle est la raison pour laquelle nous devrions créer deux listes de longueur différente pour construire un dictionnaire?
loves.by.Jesus
probablement pas, mais après cela for n in range(len(List1)) c'est un anti-pattern
Jean-François Fabre
3
  • 2018-04-18

La meilleure solution reste:

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

Transposez-le:

    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')
Calcul
la source
2

vous pouvez utiliser ce code ci-dessous:

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

Mais assurez-vous que la longueur des listes sera la même.Si la longueur n'est pas la même, la fonction zip tourne la plus longue.

Akash Nayak
la source
2

J'ai eu ce doute alors que j'essayais de résoudre un problème lié au graphique. Le problème que j'avais était que je devais définir une liste de contiguïté vide et que je voulais initialiser tous les nœuds avec une liste vide, c'est à ce moment-là que j'ai pensé que je vérifierais si elle est assez rapide, je veux dire si cela vaudrait la peine de faire une opération zip plutôt qu'une simple paire clé-valeur d'affectation. Après tout, la plupart du temps, le facteur temps est un brise-glace important. J'ai donc effectué l'opération timeit pour les deux approches.

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

Pour n_nodes = 10 000 000 je reçois,

Itération: 2,825081646999024 Sténographie: 3,535717916001886

Itération: 5.051560923002398 Sténographie: 6.255070794999483

Itération: 6.52859034499852 Sténographie: 8.221581164998497

Itération: 8.683652416999394 Sténographie: 12.599181543999293

Itération: 11.587241565001023 Sténographie: 15.27298851100204

Itération: 14.816342867001367 Sténographie: 17.162912737003353

Itération: 16.645022411001264 Sténographie: 19.976680120998935

Vous pouvez voir clairement après un certain point, l'approche d'itération à la n_ième étape dépasse le temps pris par l'approche abrégée à la n-1_ième étape.

Mayank Prakash
la source
1

Voici également un exemple d'ajout d'une valeur de liste dans votre dictionnaire

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

assurez-vous toujours que votre "Clé" (list1) est toujours dans le premier paramètre.

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

Solution comme compréhension de dictionnaire avec énumérer:

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

Solution comme pour la boucle avec énumérer:

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

Vous pouvez également essayer avec une liste qui est une combinaison de deux listes;)

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])))
Lakhan Ramawat
la source
-1

méthode sans fonction 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
la source
Salut xiyurui, L'entrée (l1 et l2) devrait être une liste. Si vous affectez l1 et l2 en tant qu'ensemble, il peut ne pas conserver l'ordre d'insertion. pour moi, j'ai obtenu la sortie comme {1: 'a', 2: 'c', 3: 'd', 4: 'b', 5: 'e'}
Nursnaaz