¿Cómo puedo hacer un diccionario (dict) a partir de listas separadas de claves y valores?

3 minutos de lectura

Avatar de usuario de Guido
Guido

Quiero combinar estos:

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

En un solo diccionario:

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

  • Mira mi respuesta @ stackoverflow.com/a/73768661/9384511

    – ABN

    19 de septiembre a las 4:58

Avatar de usuario de Dan Lenski
Dan Lenski

Como esto:

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

Voila 🙂 Los pares dict constructor y zip función son increíblemente útiles.

  • Cabe resaltar que dictionary = {zip(keys, values)} no funcionará Tienes que declarar explícitamente como dict(...)

    – Fernando Witmann

    28 de agosto de 2019 a las 15:52

  • No estoy seguro de por qué esperarías que lo hiciera, @FernandoWittmann. {thing} es azúcar sintáctico para construir un set() que contiene un elemento. {*iterable} es azúcar sintáctico para construir un set que contiene varios elementos. {k:v} o {**mapping} voluntad construir un dictpero eso es sintácticamente bastante distinto.

    – Dan Lensky

    28 de agosto de 2019 a las 16:41


  • Gracias por el comentario Dan. Tienes razón. Mi confusión ocurrió porque suelo usar la sintaxis {} para diccionarios. De hecho, si intentamos type({}) la salida es dict. Pero de hecho, si intentamos type({thing}) entonces la salida es set.

    – Fernando Witmann

    28 de agosto de 2019 a las 17:42

  • Vine aquí en caso de que podamos hacerlo mejor que {k:v for k, v in zip(keys, values)}. Resulta que podemos. +1.

    – JG

    23 de enero de 2020 a las 14:28

  • @FernandoWittmann tienes razón en que es confuso. {[thing1, thing2, … thingN]} crea un conjunto para cualquier valor de N != 0; pero para N == 0 crea un vacío dicty tienes que hacer set() para crear un conjunto vacío. Es un poco desafortunado, y PoLS-violación, verruga de Python, debido al hecho de que Python tenía dict-literales mucho antes set-literales.

    – Dan Lensky

    29 de octubre de 2020 a las 0:14


Rusia debe eliminar el avatar de usuario de Putin
Rusia debe sacar a Putin

Imagina que tienes:

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

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

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

más eficaz, dict constructor con zip

new_dict = dict(zip(keys, values))

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

dict(zip(keys, values)) requiere la búsqueda global única para cada dict y zippero 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 dictados:

Un finalista cercano al uso del constructor dict es usar la sintaxis nativa de una comprensión dict (no un lista comprensión, como otros 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, zip devuelve una lista, para evitar crear una lista innecesaria, utilice izip en su lugar (el alias de zip puede reducir los cambios de código cuando pasa 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

izip de itertools se convierte zip en Phyton 3. izip es 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 usar un iterable y evitar crear 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 zip simplemente crea un objeto iterable:

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

Dado que queremos evitar la creación de estructuras de datos innecesarias, generalmente 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 lista por comprensión, se crea innecesariamente una lista adicional. Esperaría que todos ellos tuvieran menos rendimiento, 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 del más rápido al 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 parece una mala manera de comparar el rendimiento. Seguramente mean y/o max serían indicadores mucho más útiles para el uso real.

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

Si el sistema operativo se bloquea por algún 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 meanese tipo de eventos sesgarían mucho nuestros resultados, y si usáramos max solo obtendremos el resultado más extremo, el más probablemente afectado por tal evento.

Un comentarista también dice:

En python 3.6.8, usando valores medios, la comprensión de dict es aún más rápida, aproximadamente un 30 % para estas listas pequeñas. Para listas más grandes (10k números aleatorios), el dict la llamada es un 10% más rápida.

Supongo que queremos decir dict(zip(... con 10k números aleatorios. Eso suena como un caso de uso bastante inusual. Tiene sentido que las llamadas más directas predominen en grandes conjuntos de datos, y no me sorprendería que los bloqueos del sistema operativo dominen dado el tiempo que llevaría ejecutar esa prueba, sesgando aún más sus números. Y si usas mean o max Consideraría que sus resultados no tienen sentido.

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

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

Y vemos aquí que dict(zip(... de 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

  • A mediados de 2019 (python 3.7.3), encuentro diferentes tiempos. %%timeit devuelve 1,57 \pm 0,019 microsegundos para dict(zip(headList, textList)) & 1.95 \pm 0.030 microsegundos para {k: v for k, v in zip(headList, textList)}. Sugeriría el primero por legibilidad y velocidad. Obviamente, esto llega al argumento min() vs mean() para timeit.

    – Mark_Anderson

    2 de julio de 2019 a las 15:06

  • Parece que estás diciendo que la comprensión de dict es más rápida, pero luego en la revisión de rendimiento, dict(zip(keys, values)) se ve más rápido. ¿Quizás olvidaste actualizar algo?

    – Loisaida Sam Sandberg

    8 de abril de 2020 a las 2:57

  • Nota menor (en gran parte irrelevante dada la EOL de Python 2): puede usar from future_builtins import zip como alternativa a from itertools import izip as zip eso es un poco más explícito sobre la descripción de la importación en términos de obtener Python 3 zip como reemplazo de los regulares zip. Es exactamente equivalente a ser claro (future_builtins.zip es en sí mismo sólo un alias de itertools.izip).

    – ShadowRanger

    29 de octubre de 2020 a las 18:24

Avatar de usuario de Mike Davis
mike davis

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.

  • Cierto para Python2, pero en Python 3, zip ya es económico en el consumo de memoria. docs.python.org/3/library/functions.html#zip De hecho, puedes ver que six usos zip en Python 3 para reemplazar itertools.izip en pitón 2 pythonhosted.org/six .

    – Pedro Cattori

    17 de febrero de 2017 a las 14:56


  • respuesta obsoleta

    – Julio GM

    24 ago a las 20:01

avatar de usuario de iny
eny

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

Producción:

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

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

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

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

avatar de usuario de jfs
jfs

Si necesita transformar claves o valores antes de crear un diccionario, entonces un generador de expresión puede ser usado. Ejemplo:

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

Echar un vistazo Codifique como un Pythonista: Python idiomático.

¿Ha sido útil esta solución?