Convierte dos listas en un diccionario

1229

Imagina que tienes:

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

Respuestas:

2145

Me gusta 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: https://docs.python.org/3/library/functions.html#func-dict

Dan Lenski
fuente
3
Vale la pena señalar que dictionary = {zip(keys, values)}eso no funcionará. Tienes que declarar explícitamente comodict(...)
Fernando Wittmann
55
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 un 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.
Dan Lenski
66
Gracias por el comentario Dan. Tienes razón. Mi confusión ocurrió porque usualmente uso el sintax {}para los diccionarios. De hecho, si intentamos type({})la salida es dict. Pero, de hecho, si lo intentamos, type({thing})entonces la salida es set.
Fernando Wittmann
Vine aquí en caso de que podamos hacerlo mejor que {k:v for k, v in zip(keys, values)}. Resulta que podemos. +1.
JG
140

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

dictConstructor con más rendimientozip

new_dict = dict(zip(keys, values))

En Python 3, zip ahora devuelve un iterador perezoso, y este es ahora el enfoque más eficaz.

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

Segundo puesto, comprensión dict:

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

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

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

En Python 2, zipdevuelve una lista, para evitar crear una lista innecesaria, use izipen su lugar (alias a zip puede reducir los cambios de código cuando se mueve 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 innecesaria de listas), e ideal para 2.6 o menos:

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 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 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 zipsimplemente crea un objeto iterable:

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

Como queremos evitar crear 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 primeros dos casos, se coloca una capa adicional de cómputo 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 sean menos eficientes, 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:

minParece una mala manera de comparar el rendimiento. Seguramente meany / o maxserían indicadores mucho más útiles para el uso real.

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 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 lo usáramos mean, ese tipo de eventos sesgaría nuestros resultados en gran medida, y si lo maxusáramos 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 de hecho aún más rápida, en aproximadamente un 30% para estas pequeñas listas. Para listas más grandes (10k números aleatorios), la dictllamada es aproximadamente 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 dominen en grandes conjuntos de datos, y no me sorprendería si los bloqueos del sistema operativo son dominantes dado el tiempo que tomaría ejecutar esa prueba, sesgando aún más sus números. Y si usa meano maxconsideraría sus resultados sin 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
Aaron Hall
fuente
1
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 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
1
minParece una mala manera de comparar el rendimiento. Seguramente meany / o maxserían indicadores mucho más útiles para el uso real.
naught101
1
En python 3.6.8, usando valores medios, la comprensión de dict es de hecho aún más rápida, en aproximadamente un 30% para estas pequeñas listas. Para listas más grandes (10k números aleatorios), la dictllamada es aproximadamente un 10% más rápida.
naught101
@ naught101 - Abordé tus comentarios en mi respuesta.
Aaron Hall
3
Los 10k números fueron solo una forma rápida de generar 2 largas listas de elementos únicos. La generación de la lista se realizó fuera de las estimaciones de tiempo. / / ¿Por qué crees que mean o max son inútiles? Si está haciendo esto muchas veces, entonces su tiempo promedio es ~ n * medio y el límite superior es ~ n * max. Su mínimo proporciona un límite inferior, pero a la mayoría de las personas les importa el rendimiento promedio o el peor de los casos. Si hay una gran variación, su mínimo no será representativo en la mayoría de los casos. ¿Cómo es el mínimo más significativo en un escenario del mundo real?
nada101
128

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 consumo de memoria en comparación con zip.

Mike Davis
fuente
18
Es cierto para Python2, pero en Python 3, zipya es económico en consumo de memoria. docs.python.org/3/library/functions.html#zip De hecho, puede ver que sixutiliza zipPython 3 para reemplazar itertools.izipPython 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
fuente
28

También puede usar las 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'}
Brendan Berg
fuente
17

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))}
Polla A. Fattah
fuente
a veces es la forma más rápida y a veces es más lento convertir a dictobjeto, ¿por qué es así ?, gracias amigo.
Haritsinh Gohil
10

con Python 3.x, va para comprensiones dict

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 dict aquí , un ejemplo está allí:

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

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))}
protocolo de explotación
fuente
66
falla en voz alta si List1es más largo queList2
Jean-François Fabre
@ Jean-FrançoisFabre ¿Realmente importa? ¿Cuál es la razón por la que debemos tener dos listas con diferente longitud para construir un diccionario?
amado.por.Jesús
probablemente no, pero después de esto for n in range(len(List1))es un antipatrón
Jean-François Fabre
3
  • 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'}

Tranponerlo:

    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')
Cálculo
fuente
2

puedes 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. Luego la función zip gira la más larga.

Akash Nayak
fuente
2

Tuve esta duda mientras intentaba resolver un problema relacionado con el 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, fue entonces cuando pensé en comprobar si era lo suficientemente rápido, es decir, si valdría la pena hacer una operación zip en lugar de simple par clave-valor de asignación. Después de la mayoría de las veces, el factor tiempo es un rompehielos importante. 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 Abreviatura: 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

Después de cierto punto, puede ver claramente que el enfoque de iteración en el n-ésimo paso supera el tiempo empleado por el método abreviado en el n-1 ° paso.

Mayank Prakash
fuente
1

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)

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

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

Solución como comprensión del diccionario con enumerate:

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

Solución en cuanto a bucle con enumerate:

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

También puede probar con una lista que es una combinación de dos listas;)

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
fuente
-1

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'}
xiyurui
fuente
Hola xiyurui, la entrada (l1 y l2) debe 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'}
Nursnaaz