¿Cómo convierto dos listas en un diccionario?

1479

Imagina que tienes la siguiente lista.

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

¿Cuál es la forma más sencilla de producir el siguiente diccionario?

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

Como esto:

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

Voila :-) El dictconstructor por pares y la zipfunción son increíblemente útiles.

6
  • 12
    Vale la pena señalar que dictionary = {zip(keys, values)}no funcionará. Tienes que declarar explícitamente comodict(...) 28/08/19 a las 15:52
  • 18
    No estoy seguro de por qué lo esperarías, @FernandoWittmann. {thing}es azúcar sintáctico para construir un set()elemento que contiene un elemento. {*iterable}es azúcar sintáctico para construir un que setcontiene varios elementos. {k:v}o {**mapping} va a construir una dict, pero eso es sintácticamente muy distinta. 28/08/19 a las 16:41
  • 10
    Gracias por el comentario Dan. Tienes razón. Mi confusión ocurrió porque normalmente uso el sintaxis {}para los diccionarios. De hecho, si lo intentamos, type({})la salida es dict. Pero de hecho, si lo intentamos type({thing}), la salida es set. 28/08/19 a las 17:42
  • 13
    Vine aquí en caso de que podamos hacerlo mejor que {k:v for k, v in zip(keys, values)}. Resulta que podemos. +1.
    J.G.
    23/01/20 a las 14:28
  • 2
    @FernandoWittmann tienes razón en que es confuso. {[thing1, thing2, … thingN]}crea un conjunto por cualquier valor de N != 0; pero para N == 0ello crea un vacío dict, y tienes que hacer set()para crear un conjunto vacío. Es una verruga de Python algo desafortunada y que viola PoLS, debido al hecho de que Python tenía dict-literals mucho antes que set-literals. 29/10/20 a las 0:14
179

Imagine that you have:

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

What is the simplest way to produce the following dictionary ?

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

El dictconstructor más eficiente conzip

new_dict = dict(zip(keys, values))

En Python 3, zip ahora devuelve un iterador perezoso, y este es ahora el enfoque con mayor rendimiento.

dict(zip(keys, values))requiere la búsqueda global única para cada dicty zip, pero no forma estructuras de datos intermedias innecesarias ni tiene que lidiar con búsquedas locales en la aplicación de funciones.

Subcampeón, comprensión de dict:

Un segundo lugar cercano al uso del constructor dict es usar la sintaxis nativa de una comprensión de dict (no una comprensión de lista , como otros lo han dicho erróneamente):

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

Elija esto cuando necesite mapear o filtrar según las claves o el valor.

En Python 2, zipdevuelve una lista, para evitar crear una lista innecesaria, utilice izipen su lugar (con un alias para zip puede reducir los cambios de código cuando se cambia a Python 3).

from itertools import izip as zip

Entonces eso sigue siendo (2.7):

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

Python 2, ideal para <= 2.6

izipfrom se itertoolsconvierte zipen Python 3. izipes mejor que zip para Python 2 (porque evita la creación de listas innecesarias), e ideal para 2.6 o inferior:

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

Resultado para todos los casos:

En todos los casos:

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

Explicación:

Si miramos la ayuda en dict, vemos que toma una variedad de formas de argumentos:


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

El enfoque óptimo es utilizar un iterable evitando la creación de estructuras de datos innecesarias. En Python 2, zip crea una lista innecesaria:

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

En Python 3, el equivalente sería:

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

y Python 3 zipsimplemente crea un objeto iterable:

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

Como queremos evitar la creación de estructuras de datos innecesarias, normalmente queremos evitar Python 2 zip(ya que crea una lista innecesaria).

Alternativas de menor rendimiento:

Esta es una expresión generadora que se pasa al constructor dict:

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

o equivalente:

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

Y esta es una lista de comprensión que se pasa al constructor dict:

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

En los dos primeros casos, se coloca una capa adicional de cálculo no operativo (por lo tanto innecesario) sobre el zip iterable, y en el caso de la comprensión de la lista, se crea innecesariamente una lista adicional. Esperaría que todos ellos tuvieran un rendimiento menor, y ciertamente no más.

Revisión de desempeño:

En Python 3.8.2 de 64 bits proporcionado por Nix, en Ubuntu 16.04, ordenado de más rápido a más 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)) gana incluso con pequeños conjuntos de claves y valores, pero para conjuntos más grandes, las diferencias en el rendimiento serán mayores.

Un comentarista dijo:

min seems like a bad way to compare performance. Surely mean and/or max would be much more useful indicators for real usage.

Usamos minporque estos algoritmos son deterministas. Queremos conocer el rendimiento de los algoritmos en las mejores condiciones posibles.

Si el sistema operativo se bloquea por cualquier motivo, no tiene nada que ver con lo que estamos tratando de comparar, por lo que debemos excluir ese tipo de resultados de nuestro análisis.

Si usáramos mean, ese tipo de eventos sesgaría nuestros resultados en gran medida, y si usáramos maxsolo obtendremos el resultado más extremo, el que probablemente se vea afectado por tal evento.

Un comentarista también dice:

In python 3.6.8, using mean values, the dict comprehension is indeed still faster, by about 30% for these small lists. For larger lists (10k random numbers), the dict call is about 10% faster.

Supongo que nos referimos dict(zip(...a 10k números aleatorios. Eso suena como un caso de uso bastante inusual. Tiene sentido que las llamadas más directas dominen en grandes conjuntos de datos, y no me sorprendería que los bloqueos del sistema operativo dominen dado el tiempo que tomaría ejecutar esa prueba, sesgando aún más sus números. Y si usa meano max, consideraría que sus resultados no tienen sentido.

Usemos un tamaño más realista en nuestros ejemplos principales:

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

Y vemos aquí que de dict(zip(...hecho se ejecuta más rápido para conjuntos de datos más grandes en aproximadamente un 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
3
  • 2
    A mediados de 2019 (Python 3.7.3), encuentro diferentes tiempos. %% timeit devuelve 1.57 \ pm 0.019microsec para dict(zip(headList, textList))& 1.95 \ pm 0.030 microsec for {k: v for k, v in zip(headList, textList)}. Sugeriría el primero por su legibilidad y velocidad. Obviamente, esto llega al argumento min () vs mean () para timeit. 2/07/19 a las 15:06
  • Parece que está diciendo que la comprensión de dict es más rápida, pero luego, en la revisión de rendimiento, dict(zip(keys, values))parece más rápida. ¿Quizás olvidó actualizar algo? 8 de abril de 2020 a las 2:57
  • Nota menor (en gran parte irrelevante dado el EOL de Python 2): puede usarlo from future_builtins import zipcomo una alternativa a from itertools import izip as zipeso un poco más explícito sobre la descripción de la importación en términos de obtener Python 3 zipcomo reemplazo de regular zip. Es exactamente equivalente a ser claro (en future_builtins.zipsí mismo es solo un alias de itertools.izip). 29/10/20 a las 18:24
132

Prueba esto:

>>> 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, también es más económico en el consumo de memoria en comparación con zip.

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

También puede usar comprensiones de diccionario 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'}
0
19

Una forma más natural es utilizar la comprensión del diccionario.

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')    
dict = {keys[i]: values[i] for i in range(len(keys))}
1
  • a veces es la forma más rápida y a veces es más lento convertirlo en dictobjeto, ¿por qué es así ?, gracias amigo. 8 de agosto de 2019 a las 8:24
15

Si necesita transformar claves o valores antes de crear un diccionario, se puede usar una expresión generadora . Ejemplo:

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

Eche un vistazo a Code Like a Pythonista: Idiomatic Python .

0
11

con Python 3.x, se aplica a las comprensiones de dictados

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

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

print(dic)

Más sobre las comprensiones de dictados aquí , un ejemplo está ahí:

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

Para aquellos que necesitan un código simple y no están familiarizados con zip:

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

Esto se puede hacer con una línea de código:

d = {List1[n]: List2[n] for n in range(len(List1))}
3
  • 6
    falla ruidosamente si List1es más largo queList2 13 sep 2017 a las 11:49
  • @ Jean-FrançoisFabre ¿Realmente importa? ¿Cuál es la razón por la que deberíamos crear dos listas de diferente longitud para construir un diccionario? 17/12/19 a las 22:35
  • probablemente no, pero después de esto for n in range(len(List1))es un anti-patrón 18 dic 2019 a las 12:41
4
  • 2018-04-18

La mejor solución sigue siendo:

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

Transponerlo:

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

puede usar este código a continuación:

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

Pero asegúrese de que la longitud de las listas sea la misma. Si la longitud no es la misma, entonces la función zip gira la más larga.

2

Aquí también hay un ejemplo de cómo agregar un valor de lista en su diccionario

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

asegúrese siempre de que su "Clave" (lista1) esté siempre en el primer parámetro.

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

Tuve esta duda mientras intentaba resolver un problema relacionado con un gráfico. El problema que tuve fue que necesitaba definir una lista de adyacencia vacía y quería inicializar todos los nodos con una lista vacía, ahí fue cuando pensé, ¿qué tal si verifico si es lo suficientemente rápido, quiero decir si valdrá la pena hacer una operación zip? en lugar de un par clave-valor de asignación simple. Después de todo, la mayoría de las veces, el factor tiempo es un importante rompehielos. Así que realicé la operación timeit para ambos enfoques.

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

Para n_nodes = 10,000,000 obtengo,

Iteración: 2.825081646999024 Taquigrafía: 3.535717916001886

Iteración: 5.051560923002398 Taquigrafía: 6.255070794999483

Iteración: 6.52859034499852 Taquigrafía: 8.221581164998497

Iteración: 8.683652416999394 Taquigrafía: 12.599181543999293

Iteración: 11.587241565001023 Taquigrafía: 15.27298851100204

Iteración: 14.816342867001367 Taquigrafía: 17.162912737003353

Iteración: 16.645022411001264 Taquigrafía: 19.976680120998935

Puede ver claramente después de cierto punto, el enfoque de iteración en el n_ésimo paso supera el tiempo que toma el enfoque taquigráfico en el n-1_ésimo paso.

0

Solución como comprensión de diccionario con enumerar:

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

Solución como para bucle con enumerar:

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

Si está trabajando con más de 1 conjunto de valores y desea tener una lista de dictados , puede usar esto:

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

El ejemplo de la vida real sería una lista de tuplas de una consulta de base de datos emparejada con una tupla de columnas de la misma consulta. Otras respuestas solo se proporcionaron de 1 a 1.

-1

Aunque hay varias formas de hacer esto, creo que es la forma más fundamental de abordarlo; crear un bucle y diccionario y almacenar valores en ese diccionario . En el enfoque recursivo, la idea sigue siendo la misma, pero en lugar de usar un bucle, la función se llama a sí misma hasta que llega al final. Por supuesto, existen otros enfoques como el uso dict(zip(key, value))y etc. Estas no son las soluciones más efectivas.

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)

Ambos resultados deberían imprimirse

{1: 'a', 2: 'b', 3: 'c', 4: 'd'}  
0
-1
keys = ['name', 'age', 'food']
values = ['Monty', 42, 'spam']
dic = {}
c = 0
for i in keys:
    dic[i] = values[c]
    c += 1

print(dic)
{'name': 'Monty', 'age': 42, 'food': 'spam'}
-2

método sin función 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'}
1
  • Hola xiyurui, La entrada (l1 y l2) debería ser una lista. Si asigna l1 y l2 como un conjunto, es posible que no conserve el orden de inserción. para mí obtuve el resultado como {1: 'a', 2: 'c', 3: 'd', 4: 'b', 5: 'e'} 31 de enero de 2019 a las 8:32