Converta duas listas em um dicionário


1229

Imagine que você tem:

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

Qual é a maneira mais simples de produzir o seguinte dicionário?

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

Respostas:


2145

Como isso:

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

Voila :-) O dictconstrutor e a zipfunção em pares são extremamente úteis: https://docs.python.org/3/library/functions.html#func-dict


3
Vale a pena notar que dictionary = {zip(keys, values)}não vai funcionar. Você deve declarar explicitamente como #dict(...)
Fernando Wittmann

5
Não sei por que você esperaria, @FernandoWittmann. {thing}é açúcar sintático para construir set()um elemento contendo um. {*iterable}é açúcar sintático para construir um setcontendo vários elementos. {k:v}ou {**mapping} vai construir um dict, mas isso é sintaticamente bastante distintos.
precisa

6
Obrigado pelo comentário Dan. Você está certo. Minha confusão aconteceu porque eu costumo usar a sintaxe {}para dicionários. De fato, se tentarmos, type({})a saída é dict. Mas, de fato, se tentarmos type({thing}), o resultado é set.
Fernando Wittmann

Eu vim aqui caso possamos fazer melhor que {k:v for k, v in zip(keys, values)}. Acontece que podemos. +1.
JG

140

Imagine que você tem:

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

Qual é a maneira mais simples de produzir o seguinte dicionário?

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

Melhor desempenho, dictconstrutor comzip

new_dict = dict(zip(keys, values))

No Python 3, o zip agora retorna um iterador lento e agora é a abordagem de melhor desempenho.

dict(zip(keys, values))requer a pesquisa global única para dicte zip, mas não forma estruturas de dados intermediárias desnecessárias ou precisa lidar com pesquisas locais no aplicativo de funções.

Segundo classificado, compreensão do ditado:

Um corredor próximo ao uso do construtor dict é usar a sintaxe nativa de uma compreensão de dict (não uma compreensão de lista , como outros colocaram por engano):

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

Escolha isso quando precisar mapear ou filtrar com base nas chaves ou no valor.

No Python 2, zipretorna uma lista, para evitar a criação de uma lista desnecessária, use-o izip(o alias do zip pode reduzir as alterações de código quando você passa para o Python 3).

from itertools import izip as zip

Então isso ainda é (2.7):

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

Python 2, ideal para <= 2.6

izipfrom itertoolstorna - se zipno Python 3. izipé melhor que o zip para o Python 2 (porque evita a criação desnecessária de listas) e é ideal para o 2.6 ou abaixo:

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

Resultado para todos os casos:

Em todos os casos:

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

Explicação:

Se olharmos para a ajuda dict, vemos que são necessárias várias 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)

A abordagem ideal é usar um iterável, evitando criar estruturas de dados desnecessárias. No Python 2, o zip cria uma lista desnecessária:

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

No Python 3, o equivalente seria:

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

e o Python 3 zipapenas cria um objeto iterável:

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

Como queremos evitar a criação de estruturas de dados desnecessárias, geralmente queremos evitar o Python 2 zip(pois ele cria uma lista desnecessária).

Alternativas com menos desempenho:

Esta é uma expressão geradora que está sendo passada para o construtor dict:

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

ou equivalente:

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

E esta é uma compreensão de lista sendo transmitida ao construtor de dict:

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

Nos dois primeiros casos, uma camada extra de computação não-operacional (portanto desnecessária) é colocada sobre o zip iterável e, no caso da compreensão da lista, uma lista extra é desnecessariamente criada. Eu esperaria que todos eles tivessem menos desempenho e certamente não mais.

Revisão de desempenho:

No Python 3.8.2 de 64 bits fornecido pelo Nix, no Ubuntu 16.04, ordenado do mais rápido para o mais 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)) vence mesmo com pequenos conjuntos de chaves e valores, mas para conjuntos maiores, as diferenças de desempenho se tornam maiores.

Um comentarista disse:

minparece ser uma maneira ruim de comparar o desempenho. Certamente meane / ou maxseriam indicadores muito mais úteis para uso real.

Usamos minporque esses algoritmos são determinísticos. Queremos conhecer o desempenho dos algoritmos nas melhores condições possíveis.

Se o sistema operacional travar por algum motivo, não tem nada a ver com o que estamos tentando comparar, portanto, precisamos excluir esses tipos de resultados de nossa análise.

Se usássemos mean, esses tipos de eventos distorceriam muito nossos resultados e, se os usássemos, obteríamos maxapenas o resultado mais extremo - o mais provável de ser afetado por esse evento.

Um comentarista também diz:

No python 3.6.8, usando valores médios, a compreensão do ditado é realmente ainda mais rápida, em cerca de 30% para essas pequenas listas. Para listas maiores (números aleatórios de 10k), a dictchamada é cerca de 10% mais rápida.

Presumo que queremos dizer dict(zip(...com 10k números aleatórios. Isso soa como um caso de uso bastante incomum. Faz sentido que as chamadas mais diretas dominem em grandes conjuntos de dados, e eu não ficaria surpreso se as falhas no sistema operacional estiverem dominando, dado o tempo que levaria para executar esse teste, distorcendo ainda mais seus números. E se você usar meanou maxeu consideraria seus resultados sem sentido.

Vamos usar um tamanho mais realista em nossos principais exemplos:

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

E vemos aqui que dict(zip(..., de fato, corre mais rápido em conjuntos de dados maiores em cerca de 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
Em meados de 2019 (python 3.7.3), encontro diferentes tempos. %% timeit retorna 1,57 \ pm 0,019microsec para dict(zip(headList, textList))& 1,95 \ pm 0,030 microsseg para {k: v for k, v in zip(headList, textList)}. Eu sugeriria o primeiro por legibilidade e velocidade. Obviamente, isso ocorre no argumento min () vs mean () para timeit.
Mark_Anderson

1
minparece ser uma maneira ruim de comparar o desempenho. Certamente meane / ou maxseriam indicadores muito mais úteis para uso real.
precisa saber é o seguinte

1
No python 3.6.8, usando valores médios, a compreensão do ditado é realmente ainda mais rápida, em cerca de 30% para essas pequenas listas. Para listas maiores (números aleatórios de 10k), a dictchamada é cerca de 10% mais rápida.
precisa saber é o seguinte

@ naught101 - Enviei seus comentários na minha resposta.
Aaron Hall

3
Os números de 10k eram apenas uma maneira rápida de gerar 2 longas listas de elementos únicos. A geração da lista foi feita fora das estimativas de tempo. // Por que você acha que média ou max são inúteis? Se você fizer isso várias vezes, o tempo médio será ~ n * médio e o limite superior será ~ n * máx. Seu mínimo fornece um limite inferior, mas a maioria das pessoas se preocupa com o desempenho médio ou no pior dos casos. Se houver uma alta variação, seu mínimo será totalmente não representativo da maioria dos casos. Como o mínimo é mais significativo em um cenário do mundo real?
precisa saber é o seguinte

128

Tente o seguinte:

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

No Python 2, também é mais econômico em consumo de memória em comparação com zip.


18
É verdade no Python2, mas no Python 3, zipjá é econômico no consumo de memória. docs.python.org/3/library/functions.html#zip De fato, você pode ver que o Python 3 é sixusado zippara substituir itertools.izipno Python 2 pythonhosted.org/six .
Pedro Cattori 17/02

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

28

Você também pode usar as compreensões de dicionário no 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

Uma maneira mais natural é usar a compreensão do dicionário

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

às vezes é o caminho mais rápido e às vezes é mais lento para converter em dictobjeto, por que é assim ?, obrigado cara.
Haritsinh Gohil 8/08/19


10

com o Python 3.x, vale a pena compreender o dict

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

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

print(dic)

Mais sobre a compreensão de dict aqui , um exemplo está lá:

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

8

Para aqueles que precisam de código simples e não estão familiarizados com zip:

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

Isso pode ser feito por uma linha de código:

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

6
falha alto se List1for maior queList2
Jean-François Fabre

@ Jean-FrançoisFabre Isso realmente importa? qual é a razão pela qual devemos encontrar duas listas com comprimentos diferentes para construir um dicionário?
loved.by.Jesus

provavelmente não, mas depois disso for n in range(len(List1))é um antipadrão
Jean-François Fabre

3
  • 18-04-2018

A melhor solução ainda é:

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

Transponha:

    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

você pode usar este código abaixo:

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

Mas certifique-se de que o comprimento das listas seja o mesmo. Se o comprimento não for o mesmo. A função zip será a mais longa.


2

Eu tive essa dúvida enquanto tentava resolver um problema relacionado a gráficos. O problema que tive foi que eu precisava definir uma lista de adjacência vazia e queria inicializar todos os nós com uma lista vazia. Foi quando pensei em verificar se é rápida o suficiente, quero dizer se valerá a pena fazer uma operação zip em vez de um simples par de valores-chave de atribuição. Afinal, na maioria das vezes, o fator tempo é um importante quebra-gelo. Então, realizei a operação timeit para as duas abordagens.

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, recebo,

Iteração: 2.825081646999024 Taquigrafia: 3.535717916001886

Iteração: 5.051560923002398 Taquigrafia: 6.255070794999483

Iteração: 6.52859034499852 Taquigrafia: 8.221581164998497

Iteração: 8.683652416999394 abreviação: 12.599181543999293

Iteração: 11.587241565001023 Taquigrafia: 15.27298851100204

Iteração: 14.816342867001367 Taquigrafia: 17.162912737003353

Iteração: 16.645022411001264 Taquigrafia: 19.976680120998935

Você pode ver claramente, depois de um certo ponto, que a abordagem de iteração na n-ésima etapa ultrapassa o tempo gasto pela abordagem abreviada na n-ésima etapa.


1

Aqui também está um exemplo de adição de um valor de lista no seu dicionário

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

sempre verifique se a sua "Chave" (lista1) está sempre no primeiro parâmetro.

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

0

Solução como compreensão de dicionário com enumerar:

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

Solução como loop com enumerar:

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

0

Você também pode tentar com uma lista que é uma combinação de duas 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])))

-1

método sem função 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'}

Oi xiyurui, A entrada (l1 e l2) deve ser uma lista. Se você atribuir l1 e l2 como um conjunto, pode não preservar a ordem de inserção. para mim, eu tenho a saída como {1: 'a', 2: 'c', 3: 'd', 4: 'b', 5: 'e'} '
Nursnaaz 31/01/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.