Python: TypeError: type unhashable: 'liste'

95

J'essaye de prendre un fichier qui ressemble à ceci

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

Et utilisez un dictionnaire pour que la sortie ressemble à ceci

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

C'est ce que j'ai essayé

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()

Je reçois toujours un TypeError: unhashable type: 'list'. Je sais que les clés d'un dictionnaire ne peuvent pas être des listes, mais j'essaie de transformer ma valeur en liste et non en clé. Je me demande si j'ai fait une erreur quelque part.

Keenan
la source

Réponses:

56

Comme indiqué par les autres réponses, l'erreur est due à k = list[0:j], où votre clé est convertie en liste. Une chose que vous pourriez essayer est de retravailler votre code pour tirer parti de la splitfonction:

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

Notez que si vous utilisez Python 3.x, vous devrez effectuer un ajustement mineur pour qu'il fonctionne correctement. Si vous ouvrez le fichier avec rb, vous devrez utiliser line = line.split(b'x')(ce qui garantit que vous divisez l'octet avec le type de chaîne approprié). Vous pouvez également ouvrir le fichier en utilisant with open('filename.txt', 'rU') as f:(ou même with open('filename.txt', 'r') as f:) et cela devrait fonctionner correctement.

RocketDonkey
la source
J'ai essayé ceci et j'obtiens TypeError: type str ne prend pas en charge l'API de tampon sur la ligne "line = line.split ('x')"
Keenan
1
@ user1871081 Ah, utilisez-vous Python 3.x? Je publierai une mise à jour qui devrait fonctionner avec cela.
RocketDonkey
31

Remarque: Cette réponse ne répond pas explicitement à la question posée. les autres réponses le font. Puisque la question est spécifique à un scénario et que l' exception soulevée est générale , cette réponse pointe vers le cas général.

Les valeurs de hachage ne sont que des entiers qui sont utilisés pour comparer rapidement les clés du dictionnaire lors d'une recherche dans le dictionnaire.

En interne, la hash()méthode appelle la __hash__()méthode d'un objet qui est définie par défaut pour n'importe quel objet.

Conversion d' une liste imbriquée en un ensemble

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

Cela se produit à cause de la liste à l'intérieur d'une liste qui est une liste qui ne peut pas être hachée. Ce qui peut être résolu en convertissant les listes imbriquées internes en un tuple ,

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

Hachage explicite d' une liste imbriquée

>>> 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 solution pour éviter cette erreur est de restructurer la liste pour avoir des tuples imbriqués au lieu de listes.

Tout est Vаиітy
la source
4
et si la liste est trop longue ?? semble que c'est une bonne solution mais pas assez générale
msh855
1
@ msh855 y a-t-il une limite de taille? J'ai testé le dictionnaire avec un tuple de taille 100000 et cela a bien fonctionné pour moi (j'utilise python 3.6)
Sreram
18

Vous essayez d'utiliser k(qui est une liste) comme clé pour d. Les listes sont modifiables et ne peuvent pas être utilisées comme clés de dict.

De plus, vous n'initialisez jamais les listes dans le dictionnaire, à cause de cette ligne:

if k not in d == False:

Ce qui devrait être:

if k not in d == True:

Ce qui devrait en fait être:

if k not in d:
Jesse le jeu
la source
5

La raison pour laquelle vous obtenez l' unhashable type: 'list'exception est que les k = list[0:j]ensembles ksont une "tranche" de la liste, qui est logiquement une autre liste, souvent plus courte. Ce dont vous avez besoin, c'est de n'obtenir que le premier élément de la liste, écrit ainsi k = list[0]. La même chose pour v = list[j + 1:]qui devrait être juste v = list[2]pour le troisième élément de la liste renvoyée par l'appel à readline.split(" ").

J'ai remarqué plusieurs autres problèmes probables avec le code, dont je vais en mentionner quelques-uns. Un gros problème est que vous ne voulez pas (re) initialiser davec d = {}pour chaque ligne lue dans la boucle. Un autre est que ce n'est généralement pas une bonne idée de nommer les variables de la même manière que l'un des types intégrés, car cela vous empêchera de pouvoir accéder à l'un d'entre eux si vous en avez besoin - et cela déroutant pour les autres qui sont habitués au noms désignant l'un de ces éléments standards. Pour cette raison, vous devez renommer votre variable de listvariable différemment pour éviter de tels problèmes.

Voici une version de travail de votre avec ces changements, j'ai également simplifié l' ifexpression d'instruction que vous aviez qui vérifie si la clé est déjà dans le dictionnaire - il existe des moyens implicites encore plus courts pour faire ce genre de chose, mais en utilisant un conditionnel La déclaration est très bien pour le moment.

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))

Production:

d: {'AAA': ['111', '112'], 'AAC': ['123'], 'AAB': ['111']}
Martineau
la source
0

Le TypeErrorse produit parce que kc'est une liste, puisqu'elle est créée en utilisant une tranche d'une autre liste avec la ligne k = list[0:j]. Cela devrait probablement être quelque chose comme k = ' '.join(list[0:j]), donc vous avez une chaîne à la place.

En plus de cela, votre ifdéclaration est incorrecte comme indiqué par la réponse de Jesse, qui devrait lire if k not in dou if not k in d(je préfère ce dernier).

Vous effacez également votre dictionnaire à chaque itération puisque vous avez à l' d = {}intérieur de votre forboucle.

Notez que vous ne devez pas non plus utiliser listou filecomme noms de variables, car vous masquerez les fonctions intégrées.

Voici comment je réécrirais votre code:

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:]))

La dict.setdefault()méthode ci-dessus remplace la if k not in dlogique de votre code.

Andrew Clark
la source
alors que la préférence est votre plein droit, not k in dpourrait confondre un novice car (not k) in d, k not in dsans ambiguïté
Jesse the Game
Je dirais même que c'est la manière «pythonique», telle qu'elle not inest répertoriée comme opérateur .
Jesse the Game
Ouais, je pense que ma préférence vient probablement de l'apprentissage d'autres langues d'abord, où pour quelque chose comme un test de confinement, vous n'auriez pas d'opérateurs pour cela, vous feriez donc quelque chose comme !a.contains(b). not inpeut être plus pythonique, je trouve juste le concept de deux opérateurs de mots plus déroutant que l'utilisation d'un inverse sur une expression booléenne.
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
la source