วิธีเอาชนะ 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 มีการ จำกัด ขนาดหรือไม่? ฉันทดสอบพจนานุกรมด้วย tuple ขนาด 100,000 และใช้งานได้ดีสำหรับฉัน (ฉันใช้ python 3.6)
Sreram

19

คุณกำลังพยายามที่จะใช้k(ซึ่งเป็นรายการ) dเป็นกุญแจสำคัญสำหรับ รายการไม่แน่นอนและไม่สามารถใช้เป็นคีย์คำสั่งได้

นอกจากนี้คุณจะไม่เริ่มต้นรายการในพจนานุกรมเนื่องจากบรรทัดนี้:

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

ฉันก็จะเถียงว่ามันคือ 'pythonic' ลักษณะที่not inเป็น บริษัท จดทะเบียนในฐานะที่เป็นผู้ประกอบการ
Jesse the Game

ใช่ผมคิดว่าค่าของฉันอาจจะมาจากการเรียนรู้ภาษาอื่น ๆ !a.contains(b)เป็นครั้งแรกที่บางอย่างเช่นการทดสอบการบรรจุคุณจะไม่ต้องดำเนินการสำหรับการนี้เพื่อที่คุณจะทำสิ่งที่ชอบ not inอาจเป็น pythonic มากกว่าฉันเพิ่งพบว่าแนวคิดของตัวดำเนินการคำสองคำสับสนมากกว่าการใช้ผกผันกับนิพจน์บูลีน
Andrew Clark

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.