Como inicializo um dicionário de listas vazias em Python?

89

Minha tentativa de criar programaticamente um dicionário de listas não está me permitindo endereçar individualmente as chaves do dicionário. Sempre que crio o dicionário de listas e tento anexar a uma chave, todas elas são atualizadas. Aqui está um caso de teste muito simples:

data = {}
data = data.fromkeys(range(2),[])
data[1].append('hello')
print data

Resultado atual: {0: ['hello'], 1: ['hello']}

Resultado esperado: {0: [], 1: ['hello']}

Aqui está o que funciona

data = {0:[],1:[]}
data[1].append('hello')
print data

Resultado real e esperado: {0: [], 1: ['hello']}

Por que o fromkeysmétodo não está funcionando conforme o esperado?

Martin Burch
fonte

Respostas:

112

Passar []como segundo argumento para dict.fromkeys()fornece um resultado um tanto inútil - todos os valores no dicionário serão o mesmo objeto de lista.

No Python 2.7 ou superior, você pode usar uma compreensão dicitonária:

data = {k: [] for k in range(2)}

Em versões anteriores do Python, você pode usar

data = dict((k, []) for k in range(2))
Sven Marnach
fonte
3
Bem, este é um comportamento bastante não intuitivo, alguma idéia de por que o mesmo objeto é usado para todas as chaves?
Bar
3
@Bar Porque não há mais nada que a função pudesse fazer dentro da semântica da linguagem Python. Você passa um único objeto para ser usado como valor para todas as chaves, de modo que esse único objeto seja usado para todas as chaves. Seria melhor que o fromkeys()método aceitasse uma função de fábrica, para que pudéssemos passar listcomo uma função e essa função seria chamada uma vez para cada chave criada, mas essa não é a API real de dict.fromkeys().
Sven Marnach
3
Isso não é nada intuitivo. Isso me levou uma hora para encontrar. Obrigado
Astrid
1
A mesma coisa acontece se você passar dict () como um segundo argumento. Comportamento muito desconcertante.
Orly
@Orly Isso ocorre porque primeiro um dicionário vazio é criado e, em seguida, uma referência a ele é passada para todas as inicializações.
Dr_Zaszuś
83

Em vez disso, use defaultdict :

from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')

Dessa forma, você não precisa inicializar todas as chaves que deseja usar nas listas de antemão.

O que está acontecendo em seu exemplo é que você usa uma lista (mutável):

alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print data

iria produzir {0: [1, 2], 1: [1, 2]}.

Martijn Pieters
fonte
2
No meu caso, preciso inicializar todas as chaves com antecedência para que o restante da lógica do programa funcione conforme o esperado, mas, de outra forma, essa seria uma boa solução. Obrigado.
Martin Burch
Acho que o que está faltando nessa resposta é dizer que essa solução funciona, ao contrário do OP, porque listaqui não é uma lista vazia, mas um tipo (ou você pode vê-lo como um construtor que pode ser chamado, eu acho). Portanto, toda vez que uma chave ausente é passada, uma nova lista é criada em vez de reutilizar a mesma.
Dr_Zaszuś
8

Você está preenchendo seus dicionários com referências a uma única lista, de modo que, ao atualizá-la, a atualização se reflete em todas as referências. Em vez disso, tente uma compreensão de dicionário. Veja Criar um dicionário com compreensão de lista em Python

d = {k : v for k in blah blah blah}
cobie
fonte
ótima sugestão sobre como inicializar valores de dicionário ... obrigado cobie! Estendi seu exemplo para redefinir os valores em um dicionário existente, d. Fiz isso da seguinte forma: d = {k: 0 para k em d}
João
O que há vnesta resposta?
Dr_Zaszuś
-3

Você pode usar isto:

data[:1] = ['hello']
Conner Dassen
fonte
2
Pode ser útil para o OP explicar por que isso funciona. A pergunta original postada pergunta por que não funciona como esperado.
william.taylor.09
@ william.taylor.09 é meio óbvio porque isso funciona, não é?
Conner Dassen
OP está (estava) perguntando "Por que o método fromkeys não está funcionando como esperado?"
william.taylor.09