Python: TypeError: unhashable tipo: 'lista'

95

Estoy intentando tomar un archivo que se parece a este

AAA x 111
AAB x 111
AAA x 112
AAC x 123
...

Y use un diccionario para que la salida se vea así

{AAA: ['111', '112'], AAB: ['111'], AAC: [123], ...}

Esto es lo que he probado

file = open("filename.txt", "r") 
readline = file.readline().rstrip()
while readline!= "":
    list = []
    list = readline.split(" ")
    j = list.index("x")
    k = list[0:j]
    v = list[j + 1:]
    d = {}
    if k not in d == False:
        d[k] = []
    d[k].append(v)
    readline = file.readline().rstrip()

Sigo recibiendo un TypeError: unhashable type: 'list'. Sé que las claves en un diccionario no pueden ser listas, pero estoy tratando de convertir mi valor en una lista, no en la clave. Me pregunto si cometí un error en alguna parte.

Keenan
fuente

Respuestas:

56

Como lo indican las otras respuestas, el error se debe a k = list[0:j], donde su clave se convierte en una lista. Una cosa que podría intentar es reelaborar su código para aprovechar la splitfunción:

# Using with ensures that the file is properly closed when you're done
with open('filename.txt', 'rb') as f:
  d = {}
  # Here we use readlines() to split the file into a list where each element is a line
  for line in f.readlines():
    # Now we split the file on `x`, since the part before the x will be
    # the key and the part after the value
    line = line.split('x')
    # Take the line parts and strip out the spaces, assigning them to the variables
    # Once you get a bit more comfortable, this works as well:
    # key, value = [x.strip() for x in line] 
    key = line[0].strip()
    value = line[1].strip()
    # Now we check if the dictionary contains the key; if so, append the new value,
    # and if not, make a new list that contains the current value
    # (For future reference, this is a great place for a defaultdict :)
    if key in d:
      d[key].append(value)
    else:
      d[key] = [value]

print d
# {'AAA': ['111', '112'], 'AAC': ['123'], 'AAB': ['111']}

Tenga en cuenta que si está utilizando Python 3.x, tendrá que hacer un pequeño ajuste para que funcione correctamente. Si abre el archivo con rb, necesitará usar line = line.split(b'x')(lo que asegura que está dividiendo el byte con el tipo correcto de cadena). También puede abrir el archivo usando with open('filename.txt', 'rU') as f:(o incluso with open('filename.txt', 'r') as f:) y debería funcionar bien.

CoheteBurro
fuente
Intenté esto y obtengo TypeError: type str no es compatible con la API de búfer en la línea "line = line.split ('x')"
Keenan
1
@ user1871081 Ah, ¿estás usando Python 3.x? Publicaré una actualización que debería funcionar con eso.
RocketDonkey
31

Nota: esta respuesta no responde explícitamente a la pregunta formulada. las otras respuestas lo hacen. Dado que la pregunta es específica de un escenario y la excepción planteada es general , esta respuesta apunta al caso general.

Los valores hash son solo números enteros que se utilizan para comparar rápidamente las claves del diccionario durante una búsqueda de diccionario.

Internamente, el hash()método llama al __hash__()método de un objeto que se establece de forma predeterminada para cualquier objeto.

Convertir una lista anidada en un conjunto

>>> a = [1,2,3,4,[5,6,7],8,9]
>>> set(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Esto sucede debido a la lista dentro de una lista que es una lista que no se puede usar con hash. Que se puede resolver convirtiendo las listas anidadas internas en una tupla ,

>>> set([1, 2, 3, 4, (5, 6, 7), 8, 9])
set([1, 2, 3, 4, 8, 9, (5, 6, 7)])

Hash explícitamente una lista anidada

>>> hash([1, 2, 3, [4, 5,], 6, 7])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'


>>> hash(tuple([1, 2, 3, [4, 5,], 6, 7]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

>>> hash(tuple([1, 2, 3, tuple([4, 5,]), 6, 7]))
-7943504827826258506

La solución para evitar este error es reestructurar la lista para tener tuplas anidadas en lugar de listas.

Todo es Vаиітy
fuente
4
¿y si la lista es demasiado grande? parece que es una buena solución pero no lo suficientemente general
msh855
1
@ msh855 ¿hay algún límite de tamaño? Probé el diccionario con una tupla de tamaño 100,000 y funcionó bien para mí (estoy usando Python 3.6)
Sreram
18

Estás intentando usar k(que es una lista) como clave para d. Las listas son mutables y no se pueden utilizar como claves de diccionario.

Además, nunca está inicializando las listas en el diccionario, debido a esta línea:

if k not in d == False:

Que debería ser:

if k not in d == True:

Que debería ser en realidad:

if k not in d:
Jesse el juego
fuente
5

La razón por la que obtiene la unhashable type: 'list'excepción es porque los k = list[0:j]conjuntos kson un "segmento" de la lista, que es lógicamente otra lista, a menudo más corta. Lo que necesita es obtener solo el primer elemento de la lista, escrito así k = list[0]. Lo mismo para lo v = list[j + 1:]cual debería ser solo v = list[2]para el tercer elemento de la lista devuelto por la llamada a readline.split(" ").

Noté varios otros problemas probables con el código, de los cuales mencionaré algunos. Uno importante es que no desea (re) inicializar dcon d = {}cada línea leída en el bucle. Otra es que generalmente no es una buena idea nombrar las variables de la misma manera que cualquiera de los tipos integrados porque evitará que pueda acceder a uno de ellos si lo necesita, y es confuso para otros que están acostumbrados a nombres que designan uno de estos elementos estándar. Por esa razón, debe cambiar el nombre de su variablelist variable por algo diferente para evitar problemas como ese.

Aquí hay una versión funcional de su con estos cambios, también simplifiqué la ifexpresión de declaración que tenía que verifica si la clave ya está en el diccionario; hay formas implícitas aún más cortas de hacer este tipo de cosas, pero usando un condicional La declaración está bien por ahora.

d = {}
file = open("filename.txt", "r")
readline = file.readline().rstrip()
while readline:
    lst = readline.split(" ") # Split into sequence like ['AAA', 'x', '111'].
    k = lst[0]  # First item.
    v = lst[2]  # Third item.
    if k not in d:  # New key?
        d[k] = []  # Initialize its associated value to an empty list.
    d[k].append(v)
    readline = file.readline().rstrip()

file.close()  # Done reading file.
print('d: {}'.format(d))

Salida:

d: {'AAA': ['111', '112'], 'AAC': ['123'], 'AAB': ['111']}
martineau
fuente
0

El TypeErrorestá sucediendo porque kes una lista, ya que se crea utilizando un trozo de otra lista con la línea k = list[0:j]. Esto probablemente debería ser algo comok = ' '.join(list[0:j]) , por lo que tiene una cadena en su lugar.

Además de esto, su ifdeclaración es incorrecta como lo indica la respuesta de Jesse, que debe leer if k not in doif not k in d (prefiero la última).

También está limpiando su diccionario en cada iteración, ya que tiene d = {}dentro de su forciclo.

Tenga en cuenta que tampoco debe usar listo filecomo nombres de variable, ya que enmascarará las incorporaciones.

Así es como reescribiría su código:

d = {}
with open("filename.txt", "r") as input_file:
    for line in input_file:
        fields = line.split()
        j = fields.index("x")
        k = " ".join(fields[:j])
        d.setdefault(k, []).append(" ".join(fields[j+1:]))

El dict.setdefault()método anterior reemplaza la if k not in dlógica de su código.

Andrew Clark
fuente
si bien la preferencia es su pleno derecho, not k in dpodría confundir a un novato como (not k) in d, mientras que k not in dno tiene ambigüedad
Jesse the Game
Incluso diría que es la forma 'pitónica' como not inse indica como operador .
Jesse the Game
Sí, creo que mi preferencia probablemente proviene de aprender otros idiomas primero, donde para algo como una prueba de contención no tendrías operadores para esto, así que harías algo así !a.contains(b). not inpuede ser más pitónico, solo encuentro el concepto de operadores de dos palabras más confuso que usar un inverso en una expresión booleana.
Andrew Clark
-1
    python 3.2

    with open("d://test.txt") as f:
              k=(((i.split("\n"))[0].rstrip()).split() for i in f.readlines())
              d={}
              for i,_,v in k:
                      d.setdefault(i,[]).append(v)
raton
fuente