Как преодолеть TypeError: unhashable type: 'list'

97

Пытаюсь взять файл, который выглядит так:

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

И используйте словарь, чтобы результат выглядел так

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

Это то, что я пробовал

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

Я продолжаю получать TypeError: unhashable type: 'list'. Я знаю, что ключи в словаре не могут быть списками, но я пытаюсь превратить свое значение в список, а не в ключ. Интересно, не ошибся ли я где-нибудь.

Кинан
источник

Ответы:

56

Как указано в других ответах, ошибка связана с тем k = list[0:j], где ваш ключ преобразуется в список. Вы можете попробовать переработать код, чтобы воспользоваться этой splitфункцией:

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

Обратите внимание: если вы используете Python 3.x, вам придется внести незначительные изменения, чтобы он работал правильно. Если вы откроете файл с помощью rb, вам нужно будет использовать line = line.split(b'x')(что гарантирует, что вы разделяете байт с правильным типом строки). Вы также можете открыть файл с помощью with open('filename.txt', 'rU') as f:(или даже with open('filename.txt', 'r') as f:), и он должен работать нормально.

РакетаОбезьяна
источник
33

Примечание. Этот ответ не является прямым ответом на заданный вопрос. другие ответы делают это. Поскольку вопрос относится к конкретному сценарию, а возникшее исключение является общим , этот ответ указывает на общий случай.

Значения хэша - это просто целые числа, которые используются для быстрого сравнения ключей словаря во время поиска по словарю.

Внутри hash()метод вызывает __hash__()метод объекта, который по умолчанию установлен для любого объекта.

Преобразование вложенного списка в набор

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

Это происходит из-за списка внутри списка, который не может быть хеширован. Это можно решить, преобразовав внутренние вложенные списки в кортеж ,

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

Явное хеширование вложенного списка

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

Чтобы избежать этой ошибки, можно реструктурировать список так, чтобы вместо списков были вложенные кортежи.

Все вайти
источник
4
что делать, если список слишком велик ?? похоже, что это хорошее решение, но недостаточно общее
msh855
1
@ msh855 есть ли ограничение на размер? Я протестировал словарь с кортежем размером 100000, и у меня он работал нормально (я использую python 3.6)
Sreram
19

Вы пытаетесь использовать k(это список) в качестве ключа для d. Списки изменяемы и не могут использоваться в качестве ключей dict.

Кроме того, вы никогда не инициализируете списки в словаре из-за этой строки:

if k not in d == False:

Что должно быть:

if k not in d == True:

Что на самом деле должно быть:

if k not in d:
Джесси Игра
источник
6

Причина вы получаете unhashable type: 'list'исключение, потому что k = list[0:j]наборы kбыть «срез» из списка, который является логически другим, часто короче, списком. Что вам нужно, так это получить только первый элемент в списке, написанный таким образом k = list[0]. То же самое, v = list[j + 1:]что и v = list[2]для третьего элемента списка, возвращаемого при вызове readline.split(" ").

Я заметил несколько других вероятных проблем с кодом, из которых я упомяну несколько. Большой один вы не хотите (ре) инициализации dс d = {}для каждой строки прочитать в цикле. Во-вторых, обычно не рекомендуется называть переменные так же, как любые встроенные типы, потому что это помешает вам получить доступ к одному из них, если он вам нужен - и это сбивает с толку других, которые привыкли к имена, обозначающие один из этих стандартных предметов. По этой причине вам следует переименовать вашу переменную в listдругое имя, чтобы избежать подобных проблем.

Вот ваша рабочая версия с этими изменениями. Я также заменил ifвыражение оператора, которое вы использовали для проверки, был ли ключ уже в словаре, и теперь использую setdefault()метод словаря, чтобы сделать то же самое немного более лаконично.

d = {}
with open("nameerror.txt", "r") as file:
    line = file.readline().rstrip()
    while line:
        lst = line.split() # Split into sequence like ['AAA', 'x', '111'].
        k, _, v = lst[:3]  # Get first and third items.
        d.setdefault(k, []).append(v)
        line = file.readline().rstrip()

print('d: {}'.format(d))

Выход:

d: {'AAA': ['111', '112'], 'AAC': ['123'], 'AAB': ['111']}
Мартино
источник
0

Это TypeErrorпроисходит потому, что kэто список, поскольку он создается с использованием фрагмента из другого списка со строкой k = list[0:j]. Вероятно, это должно быть что-то вроде k = ' '.join(list[0:j]), так что вместо этого у вас есть строка.

В дополнение к этому ваше ifутверждение неверно, как указано в ответе Джесси, который следует читать if k not in dили if not k in d(я предпочитаю последнее).

Вы также очищаете свой словарь на каждой итерации, поскольку у вас d = {}внутри forцикла.

Обратите внимание, что вы также не должны использовать listили в fileкачестве имен переменных, поскольку вы будете маскировать встроенные функции.

Вот как я бы переписал ваш код:

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

Приведенный dict.setdefault()выше метод заменяет if k not in dлогику вашего кода.

Эндрю Кларк
источник
в то время как предпочтение ваше полное право, not k in dможет запутать новичка , как (not k) in d, в то время как k not in dне имеет двусмысленность
Jesse Игра
Я бы даже сказал, что это «питонический» способ, not inуказанный как оператор .
Игра Джесси,
Да, я думаю, что мое предпочтение, вероятно, исходит из изучения в первую очередь других языков, где для чего-то вроде теста на сдерживание у вас не было бы операторов для этого, поэтому вы бы сделали что-то вроде !a.contains(b). not inможет быть более питоническим, я просто нахожу концепцию двух операторов более запутанной, чем использование инверсии для логического выражения.
Эндрю Кларк
-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)
ратон
источник