Python: TypeError: unhashable type: 'list'


95

Я намагаюся взяти файл, який виглядає так

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:), і він повинен працювати нормально.


Я спробував це, і я отримую TypeError: тип str не підтримує API буфера в рядку "line = line.split ('x')"
Кінан

1
@ user1871081 Ах, ти використовуєш Python 3.x? Я опублікую оновлення, яке має працювати з цим.
RocketDonkey

31

Примітка: Ця відповідь не відповідає явно на поставлене запитання. інші відповіді це роблять. Оскільки питання стосується конкретного сценарію, а виняток - загальний , ця відповідь вказує на загальний випадок.

Значення хешу - це просто цілі числа, які використовуються для швидкого порівняння ключів словника під час пошуку словника.

Внутрішньо 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 чи існує обмеження розміру? Я протестував словник із набором розміром 100 000, і він у мене
справно

18

Ви намагаєтеся використовувати k(це список) як ключ для d. Списки можна змінювати, і їх не можна використовувати як клавіші дикту.

Крім того, ви ніколи не ініціалізуєте списки у словнику через цей рядок:

if k not in d == False:

Що повинно бути:

if k not in d == True:

Що насправді має бути:

if k not in d:

5

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

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

Ось робоча версія вашого з цими змінами в ньому, я також спростив ifвираз виразу, який перевіряє, чи є ключ уже у словнику - існують ще коротші неявні способи зробити подібні речі, але використовуючи умовний заява наразі чудова.

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

Вихід:

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не має двозначності
Джессі Гра

Я навіть міг би стверджувати, що це "пітонічний" спосіб, not inякий вказаний як оператор .
Jesse the Game

Так, я думаю, що моя перевага, мабуть, походить від вивчення інших мов спочатку, де для чогось на зразок тесту стримування у вас не буде операторів для цього, тому ви зробите щось подібне !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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.