Estoy un poco confundido acerca de lo que se puede / no se puede usar como clave para un dictado de 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
Entonces, una tupla es un tipo inmutable, pero si oculto una lista dentro de ella, entonces no puede ser una clave ... ¿No podría ocultar fácilmente una lista dentro de un módulo?
Tenía una vaga idea de que la clave tiene que ser "modificable", pero solo voy a admitir mi propia ignorancia sobre los detalles técnicos; No sé qué está pasando realmente aquí. ¿Qué saldría mal si intentara usar listas como claves, con el hash como, digamos, su ubicación de memoria?
Respuestas:
Hay un buen artículo sobre el tema en la wiki de Python: Por qué las listas no pueden ser claves de diccionario . Como se explica allí:
Se puede hacer sin romper realmente ninguno de los requisitos, pero conduce a un comportamiento inesperado. Las listas se tratan generalmente como si su valor se derivara de los valores de su contenido, por ejemplo, cuando se verifica la (des) igualdad. Muchos esperarían, comprensiblemente, que pueda usar cualquier lista
[1, 2]
para obtener la misma clave, donde tendría que mantener exactamente el mismo objeto de lista. Pero la búsqueda por valor se rompe tan pronto como se modifica una lista utilizada como clave, y para la búsqueda por identidad es necesario que mantenga exactamente la misma lista, que no es necesaria para ninguna otra operación de lista común (al menos ninguna que se me ocurra ).Otros objetos, como los módulos,
object
hacen un trato mucho más importante con su identidad de objeto de todos modos (¿cuándo fue la última vez que se llamaron dos objetos de módulo distintossys
?), Y de todos modos se los compara. Por lo tanto, es menos sorprendente, o incluso esperado, que, cuando se usan como claves de dictado, también se comparen por identidad en ese caso.fuente
¿Por qué no puedo usar una lista como clave de dictado en Python?
(para cualquiera que se tropiece con esta pregunta buscando una forma de evitarla)
como explicaron otros aquí, de hecho no puede. Sin embargo, puede usar su representación de cadena en su lugar si realmente desea usar su lista.
fuente
__eq__
. Pero si los convierte en cadenas, todo se compara por su representación de cadena.Acabo de descubrir que puede cambiar la Lista a una tupla y luego usarla como claves.
fuente
El problema es que las tuplas son inmutables y las listas no. Considera lo siguiente
¿Qué debería
d[li]
devolver? ¿Es la misma lista? ¿Qué tald[[1,2,3]]
? Tiene los mismos valores, pero ¿es una lista diferente?En definitiva, no hay una respuesta satisfactoria. Por ejemplo, si la única clave que funciona es la clave original, entonces, si no tiene ninguna referencia a esa clave, nunca más podrá acceder al valor. Con cualquier otra clave permitida, puede construir una clave sin una referencia al original.
Si ambas sugerencias funcionan, entonces tiene claves muy diferentes que devuelven el mismo valor, lo cual es más que sorprendente. Si solo funciona el contenido original, la clave se estropeará rápidamente, ya que las listas están hechas para ser modificadas.
fuente
d[li]
permanecer 5. Med[[1,2,3]]
referiría a un objeto de lista diferente como clave, por lo que sería un KeyError. Realmente no veo ningún problema todavía ... excepto que dejar que una clave sea recolectada puede hacer que algunos de los valores de dict sean inaccesibles. Pero ese es un problema práctico, no un problema lógico ..d[list(li)]
ser un KeyError es parte del problema. En casi todos los demás casos de uso ,li
sería indistinguible de una nueva lista con contenido idéntico. Funciona, pero para muchos es contrario a la intuición. Además, ¿cuándo fue la última vez que realmente tuvo que usar una lista como clave de dictado? El único caso de uso que puedo imaginar es cuando de todos modos estás hash todo por identidad, y en ese caso deberías hacer eso en lugar de confiar en__hash__
y__eq__
basar tu identidad.Aquí hay una respuesta http://wiki.python.org/moin/DictionaryKeys
Buscar diferentes listas con el mismo contenido produciría resultados diferentes, aunque comparar listas con el mismo contenido las indicaría como equivalentes.
¿Qué pasa con el uso de un literal de lista en una búsqueda de diccionario?
fuente
Su awnser se puede encontrar aquí:
Fuente y más información: http://wiki.python.org/moin/DictionaryKeys
fuente
Debido a que las listas son mutables, las
dict
claves (y losset
miembros) deben ser hash, y el hash de objetos mutables es una mala idea porque los valores hash deben calcularse sobre la base de atributos de instancia.En esta respuesta, daré algunos ejemplos concretos, con suerte agregando valor a las respuestas existentes. Cada conocimiento se aplica también a los elementos de la
set
estructura de datos.Ejemplo 1 : hash de un objeto mutable donde el valor hash se basa en una característica mutable del objeto.
Después de mutar
stupid
, ya no se puede encontrar en el dict porque el hash cambió. Solo un escaneo lineal sobre la lista de claves del dict encuentrastupid
.Ejemplo 2 : ... pero ¿por qué no solo un valor hash constante?
Tampoco es una buena idea porque los objetos iguales deben tener un hash idéntico de modo que pueda encontrarlos en un
dict
oset
.Ejemplo 3 : ... ok, ¿qué pasa con los hash constantes en todas las instancias?
Las cosas parecen funcionar como se esperaba, pero piense en lo que está sucediendo: cuando todas las instancias de su clase producen el mismo valor hash, tendrá una colisión hash siempre que haya más de dos instancias como claves en a
dict
o presentes en aset
.Encontrar la instancia correcta con
my_dict[key]
okey in my_dict
(oitem in my_set
) necesita realizar tantas verificaciones de igualdad como instancias destupidlist3
en las claves del dict (en el peor de los casos). En este punto, el propósito del diccionario - búsqueda O (1) - está completamente derrotado. Esto se demuestra en los siguientes tiempos (realizados con IPython).Algunos tiempos para el ejemplo 3
Como puede ver, la prueba de membresía en nuestro
stupidlists_set
es incluso más lenta que un escaneo lineal en su totalidadlists_list
, mientras que tiene el tiempo de búsqueda súper rápido esperado (factor 500) en un conjunto sin muchas colisiones de hash.TL; DR: se puede usar
tuple(yourlist)
comodict
claves, porque las tuplas son inmutables y se pueden usar con hash.fuente
x
yz
es el mismo. Si algo al respecto no está claro, abra una nueva pregunta.hash(x)
yhash(z)
.La respuesta simple a su pregunta es que la lista de clases no implementa el método hash que se requiere para cualquier objeto que desee ser utilizado como clave en un diccionario. Sin embargo, la razón por la que el hash no se implementa de la misma manera en, digamos, la clase tupla (según el contenido del contenedor) es porque una lista es mutable, por lo que editar la lista requeriría recalcular el hash, lo que puede significar que la lista en ahora ubicado en el cubo incorrecto dentro de la tabla hash subyacente. Tenga en cuenta que, dado que no puede modificar una tupla (inmutable), no se encuentra con este problema.
Como nota al margen, la implementación real de la búsqueda de dictobjects se basa en el Algoritmo D de Knuth Vol. 3, sec. 6.4. Si tiene ese libro a su disposición, puede que valga la pena leerlo; además, si está realmente interesado, puede que le guste echar un vistazo a los comentarios de los desarrolladores sobre la implementación real de dictobject aquí. Entra en gran detalle sobre cómo funciona exactamente. También hay una conferencia de Python sobre la implementación de diccionarios que puede interesarle. En los primeros minutos, se repasa la definición de una clave y lo que es un hash.
fuente
Según la documentación de Python 2.7.2:
Una tupla es inmutable en el sentido de que no puede agregar, eliminar o reemplazar sus elementos, pero los elementos en sí pueden ser mutables. El valor hash de la lista depende de los valores hash de sus elementos, por lo que cambia cuando cambia los elementos.
El uso de id's para hashes de listas implicaría que todas las listas se comparan de manera diferente, lo que sería sorprendente e inconveniente.
fuente
hash = id
no rompe el invariante al final del primer párrafo, la pregunta es por qué no se hace de esa manera.algo como (código psuedo):
Si se pregunta cuáles son las opciones disponibles que se pueden utilizar como clave para su diccionario. Luego
Puedes probar :
Si funciona bien, se puede usar como clave para su diccionario o convertirlo en algo que pueda tener un hash.
En breve :
tuple(<your list>)
.str(<your list>)
.fuente
dict
las claves deben ser hash. Las listas son mutables y no proporcionan un método hash válido .fuente