Cara mengatasi TypeError: tipe yang tidak dapat di-hash: 'daftar'

97

Saya mencoba mengambil file yang terlihat seperti ini:

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

Dan gunakan kamus agar keluarannya terlihat seperti ini

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

Inilah yang saya coba

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

Saya terus mendapatkan file TypeError: unhashable type: 'list'. Saya tahu bahwa kunci dalam kamus tidak bisa menjadi daftar tetapi saya mencoba membuat nilai saya menjadi daftar, bukan kuncinya. Saya bertanya-tanya apakah saya membuat kesalahan di suatu tempat.

Keenan
sumber

Jawaban:

56

Seperti yang ditunjukkan oleh jawaban lain, kesalahannya adalah karena k = list[0:j], di mana kunci Anda diubah menjadi daftar. Satu hal yang dapat Anda coba adalah mengerjakan ulang kode Anda untuk memanfaatkan splitfungsi:

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

Perhatikan bahwa jika Anda menggunakan Python 3.x, Anda harus melakukan sedikit penyesuaian agar dapat berfungsi dengan baik. Jika Anda membuka file dengan rb, Anda harus menggunakan line = line.split(b'x')(yang memastikan Anda membagi byte dengan jenis string yang tepat). Anda juga dapat membuka file menggunakan with open('filename.txt', 'rU') as f:(atau bahkan with open('filename.txt', 'r') as f:) dan itu akan berfungsi dengan baik.

RocketDonkey
sumber
33

Catatan: Jawaban ini tidak secara eksplisit menjawab pertanyaan yang diajukan. jawaban lain melakukannya. Karena pertanyaan khusus untuk skenario dan pengecualian yang diajukan bersifat umum , Jawaban ini menunjuk pada kasus umum.

Nilai hash hanyalah bilangan bulat yang digunakan untuk membandingkan kunci kamus selama pencarian kamus dengan cepat.

Secara internal, hash()metode memanggil __hash__()metode objek yang ditetapkan secara default untuk objek apa pun.

Mengonversi daftar bertingkat menjadi satu set

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

Ini terjadi karena daftar di dalam daftar yang merupakan daftar yang tidak dapat di-hash. Yang dapat diselesaikan dengan mengubah daftar bersarang internal menjadi tuple ,

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

Mencirikan daftar bertingkat secara eksplisit

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

Solusi untuk menghindari kesalahan ini adalah dengan merestrukturisasi daftar agar memiliki tupel bersarang, bukan daftar.

Semua Vаиітy
sumber
4
bagaimana jika daftarnya terlalu besar ?? terlihat itu adalah solusi yang baik tetapi tidak cukup umum
msh855
1
@ msh855 apakah ada batasan ukuran? Saya menguji kamus dengan tupel berukuran 100.000 dan berfungsi dengan baik untuk saya (saya menggunakan python 3.6)
Sreram
19

Anda mencoba menggunakan k(yang merupakan daftar) sebagai kunci untuk d. Daftar bisa berubah dan tidak bisa digunakan sebagai kunci dikt.

Selain itu, Anda tidak pernah menginisialisasi daftar di kamus, karena baris ini:

if k not in d == False:

Yang seharusnya:

if k not in d == True:

Yang seharusnya:

if k not in d:
Jesse the Game
sumber
6

Alasan Anda mendapatkan unhashable type: 'list'pengecualian adalah karena k = list[0:j]set kmenjadi "bagian" dari daftar, yang secara logis merupakan daftar yang lain, seringkali lebih pendek. Yang Anda butuhkan hanyalah mendapatkan item pertama dalam daftar, ditulis seperti itu k = list[0]. Hal yang sama v = list[j + 1:]seharusnya hanya v = list[2]untuk elemen ketiga dari daftar yang dikembalikan dari panggilan ke readline.split(" ").

Saya memperhatikan beberapa kemungkinan masalah lain dengan kode, yang akan saya sebutkan beberapa. Yang besar adalah Anda tidak ingin (re) inisialisasi ddengan d = {}untuk setiap baris dibaca dalam lingkaran. Yang lain adalah umumnya bukanlah ide yang baik untuk menamai variabel yang sama dengan jenis built-in mana pun karena itu akan mencegah Anda untuk dapat mengakses salah satunya jika Anda membutuhkannya - dan itu membingungkan orang lain yang terbiasa dengan nama yang menunjuk salah satu item standar ini. Oleh karena itu, Anda harus mengganti nama variabel listvariabel Anda dengan sesuatu yang berbeda untuk menghindari masalah seperti itu.

Berikut adalah versi kerja Anda dengan perubahan ini di dalamnya, saya juga mengganti ifekspresi pernyataan yang Anda gunakan untuk memeriksa untuk melihat apakah kuncinya sudah ada di kamus dan sekarang menggunakan setdefault()metode kamus untuk menyelesaikan hal yang sama sedikit lebih ringkas.

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

Keluaran:

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

The TypeErrorterjadi karena kadalah daftar, karena dibuat menggunakan sepotong dari daftar lain dengan garis k = list[0:j]. Ini mungkin harus seperti k = ' '.join(list[0:j]), jadi Anda memiliki string sebagai gantinya.

Selain itu, ifpernyataan Anda tidak benar seperti yang dicatat oleh jawaban Jesse, yang seharusnya dibaca if k not in datau if not k in d(Saya lebih suka yang terakhir).

Anda juga membersihkan kamus Anda pada setiap iterasi karena Anda memiliki d = {}bagian dalam forloop Anda .

Perhatikan bahwa Anda juga tidak boleh menggunakan listatau filesebagai nama variabel, karena Anda akan menutupi bawaan.

Inilah cara saya menulis ulang kode Anda:

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

The dict.setdefault()metode di atas menggantikan if k not in dlogika dari kode Anda.

Andrew Clark
sumber
sementara preferensi adalah hak Anda sepenuhnya, not k in ddapat membingungkan pemula karena (not k) in d, sementara k not in dtidak memiliki ambiguitas
Jesse the Game
Saya bahkan berpendapat bahwa itu adalah cara 'pythonic' seperti not inyang terdaftar sebagai operator .
Jesse the Game
Ya, saya pikir preferensi saya mungkin berasal dari mempelajari bahasa lain terlebih dahulu, di mana untuk sesuatu seperti tes penahanan Anda tidak akan memiliki operator untuk ini sehingga Anda akan melakukan sesuatu seperti !a.contains(b). not inmungkin lebih pythonic, saya hanya menemukan konsep operator dua kata lebih membingungkan daripada menggunakan invers pada ekspresi boolean.
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
sumber