Estou um pouco confuso sobre o que pode / não pode ser usado como uma chave para um dicionário python.
dicked = {}
dicked[None] = 'foo' # None ok
dicked[(1,3)] = 'baz' # tuple ok
import sys
dicked[sys] = 'bar' # wow, even a module is ok !
dicked[(1,[3])] = 'qux' # oops, not allowed
Portanto, uma tupla é um tipo imutável, mas se eu esconder uma lista dentro dela, então não pode ser uma chave .. Eu não poderia facilmente esconder uma lista dentro de um módulo?
Eu tinha uma vaga idéia de que a chave tinha que ser "hashble", mas vou apenas admitir minha própria ignorância sobre os detalhes técnicos; Eu não sei o que realmente está acontecendo aqui. O que daria errado se você tentasse usar listas como chaves, com o hash como, digamos, sua localização na memória?
Respostas:
Há um bom artigo sobre o assunto no wiki do Python: Por que as listas não podem ser chaves de dicionário . Conforme explicado lá:
Isso pode ser feito sem realmente quebrar nenhum dos requisitos, mas leva a um comportamento inesperado. As listas são geralmente tratadas como se seu valor fosse derivado dos valores de seu conteúdo, por exemplo, ao verificar a (in) igualdade. Muitos - compreensivelmente - esperariam que você pudesse usar qualquer lista
[1, 2]
para obter a mesma chave, onde você teria que manter exatamente o mesmo objeto de lista. Mas a pesquisa por valor quebra assim que uma lista usada como chave é modificada, e a pesquisa por identidade exige que você mantenha exatamente a mesma lista - o que não é necessário para qualquer outra operação de lista comum (pelo menos nenhuma que eu consiga pensar )Outros objetos, como módulos e
object
tornam muito mais importante sua identidade de objeto (quando foi a última vez que você teve dois objetos de módulo distintos chamadossys
?), E são comparados por isso de qualquer maneira. Portanto, é menos surpreendente - ou mesmo esperado - que eles, quando usados como chaves de ditado, comparem por identidade também nesse caso.fonte
Por que não posso usar uma lista como uma chave de dicionário em python?
(para qualquer um que tropeçar nesta questão procurando uma maneira de contornar isso)
como explicado por outros aqui, na verdade você não pode. No entanto, você pode usar sua representação de string, se realmente quiser usar sua lista.
fonte
__eq__
. Mas se você convertê-los em strings, tudo é comparado por sua representação em string.Acabei de descobrir que você pode transformar List em tupla e usá-la como chaves.
fonte
O problema é que as tuplas são imutáveis, e as listas não. Considere o seguinte
O que deve
d[li]
retornar? É a mesma lista? Que tald[[1,2,3]]
? Tem os mesmos valores, mas é uma lista diferente?Em última análise, não há uma resposta satisfatória. Por exemplo, se a única chave que funciona é a chave original, então, se você não tiver nenhuma referência a essa chave, nunca poderá acessar o valor novamente. Com todas as outras chaves permitidas, você pode construir uma chave sem uma referência ao original.
Se ambas as sugestões funcionarem, você tem chaves muito diferentes que retornam o mesmo valor, o que é mais do que surpreendente. Se apenas o conteúdo original funcionar, sua chave irá rapidamente estragar, já que as listas são feitas para serem modificadas.
fonte
d[li]
que permaneça 5.d[[1,2,3]]
se referiria a um objeto de lista diferente como a chave, então seria um KeyError. Eu realmente não vejo nenhum problema ainda .. exceto que deixar uma chave ser coletada como lixo pode tornar alguns dos valores de dicionário inacessíveis. Mas esse é um problema prático, não um problema lógico ..d[list(li)]
ser um KeyError é parte do problema. Em quase todos os outros casos de uso ,li
seria indistinguível de uma nova lista com conteúdo idêntico. Funciona, mas é contra-intuitivo para muitos. Além disso, quando foi a última vez que você realmente teve que usar uma lista como chave de dicionário? O único caso de uso que posso imaginar é quando você está fazendo hash de tudo por identidade de qualquer maneira e, nesse caso, você deve apenas fazer isso em vez de confiar__hash__
e__eq__
ser baseado em identidade.Aqui está uma resposta http://wiki.python.org/moin/DictionaryKeys
Procurar listas diferentes com o mesmo conteúdo produziria resultados diferentes, embora comparar listas com o mesmo conteúdo as indicasse como equivalentes.
Que tal usar um literal de lista em uma pesquisa de dicionário?
fonte
Seu awnser pode ser encontrado aqui:
Fonte e mais informações: http://wiki.python.org/moin/DictionaryKeys
fonte
Como as listas são mutáveis, as
dict
chaves (e osset
membros) precisam ser hash, e o hash de objetos mutáveis é uma má ideia porque os valores de hash devem ser calculados com base nos atributos da instância.Nesta resposta, darei alguns exemplos concretos, espero agregar valor às respostas existentes. Cada percepção se aplica aos elementos da
set
estrutura de dados também.Exemplo 1 : hash de um objeto mutável onde o valor de hash é baseado em uma característica mutável do objeto.
Após a mutação
stupid
, ele não pode mais ser encontrado no dicionário porque o hash mudou. Apenas uma varredura linear sobre a lista de achados de chaves do dictstupid
.Exemplo 2 : ... mas por que não apenas um valor de hash constante?
Isso também não é uma boa ideia, porque objetos iguais devem hash de forma idêntica para que você possa encontrá-los em um
dict
ouset
.Exemplo 3 : ... ok, que tal hashes constantes em todas as instâncias ?!
As coisas parecem funcionar conforme o esperado, mas pense no que está acontecendo: quando todas as instâncias de sua classe produzem o mesmo valor de hash, você terá uma colisão de hash sempre que houver mais de duas instâncias como chaves em a
dict
ou presentes em aset
.Encontrar a instância certa com
my_dict[key]
oukey in my_dict
(ouitem in my_set
) precisa realizar tantas verificações de igualdade quantas forem as instâncias destupidlist3
nas chaves do dicionário (no pior caso). Nesse ponto, o objetivo do dicionário - pesquisa O (1) - foi completamente derrotado. Isso é demonstrado nas seguintes temporizações (feitas com IPython).Alguns tempos para o exemplo 3
Como você pode ver, o teste de associação em nosso
stupidlists_set
é ainda mais lento do que uma varredura linear no todolists_list
, enquanto você tem o tempo de pesquisa super rápido esperado (fator 500) em um conjunto sem muitas colisões de hash.TL; DR: você pode usar
tuple(yourlist)
comodict
chaves, porque as tuplas são imutáveis e hashable.fonte
x
ez
é o mesmo. Se algo sobre isso não estiver claro, abra uma nova pergunta.hash(x)
ehash(z)
.A resposta simples à sua pergunta é que a lista de classes não implementa o método hash que é necessário para qualquer objeto que deseja ser usado como uma chave em um dicionário. No entanto, a razão pela qual o hash não é implementado da mesma maneira que é, digamos que a classe de tupla (com base no conteúdo do contêiner) é porque uma lista é mutável, portanto, a edição da lista exigiria que o hash fosse recalculado, o que pode significar que a lista em agora localizado no balde errado na tabela de hash subjacente. Observe que, como você não pode modificar uma tupla (imutável), esse problema não ocorre.
Como uma observação lateral, a implementação real da pesquisa de dictobjetos é baseada no Algoritmo D de Knuth Vol. 3, Seç. 6,4 Se você tem esse livro disponível, pode valer a pena ler; além disso, se você estiver realmente interessado, pode dar uma olhada nos comentários do desenvolvedor sobre a implementação real do dictobjeto aqui. Ele fornece detalhes sobre como funciona exatamente. Há também uma palestra python sobre a implementação de dicionários que pode ser do seu interesse. Eles passam pela definição de uma chave e o que é um hash nos primeiros minutos.
fonte
De acordo com a documentação do Python 2.7.2:
Uma tupla é imutável no sentido de que você não pode adicionar, remover ou substituir seus elementos, mas os próprios elementos podem ser mutáveis. O valor de hash da lista depende dos valores de hash de seus elementos e, portanto, ele muda quando você altera os elementos.
Usar id's para hashes de lista implicaria que todas as listas seriam comparadas de maneira diferente, o que seria surpreendente e inconveniente.
fonte
hash = id
não quebra o invariante no final do primeiro parágrafo, a questão é por que não é feito dessa forma.algo como (código psuedo):
Se você está se perguntando quais são as opções disponíveis que podem ser usadas como chave para o seu dicionário. Então
Podes tentar :
Se funcionar bem, pode ser usado como chave para o seu dicionário ou então convertê-lo em algo hashble.
Em resumo :
tuple(<your list>)
.str(<your list>)
.fonte
dict
as chaves precisam ser hashable. As listas são mutáveis e não fornecem um método hash válido .fonte