Python: TypeError: loại không chỉnh sửa được: 'list'


95

Tôi đang cố lấy một tệp giống như thế này

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

Và sử dụng từ điển để đầu ra giống như thế này

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

Đây là những gì tôi đã thử

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

Tôi tiếp tục nhận được một TypeError: unhashable type: 'list'. Tôi biết rằng các khóa trong từ điển không thể là danh sách nhưng tôi đang cố gắng biến giá trị của mình thành một danh sách không phải là khóa. Tôi tự hỏi liệu tôi có mắc lỗi ở đâu đó không.

Câu trả lời:


56

Như được chỉ ra bởi các câu trả lời khác, lỗi là do k = list[0:j]khóa của bạn được chuyển đổi thành danh sách ở đâu. Một điều bạn có thể thử là làm lại mã của mình để tận dụng splitchức năng:

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

Lưu ý rằng nếu bạn đang sử dụng Python 3.x, bạn sẽ phải thực hiện một điều chỉnh nhỏ để nó hoạt động bình thường. Nếu bạn mở tệp bằng rb, bạn sẽ cần sử dụng line = line.split(b'x')(điều này đảm bảo rằng bạn đang tách byte bằng loại chuỗi thích hợp). Bạn cũng có thể mở tệp bằng with open('filename.txt', 'rU') as f:(hoặc thậm chí with open('filename.txt', 'r') as f:) và nó sẽ hoạt động tốt.


Tôi đã thử điều này và tôi nhận được TypeError: type str không hỗ trợ API bộ đệm trên dòng "line = line.split ('x')"
Keenan

1
@ user1871081 À, bạn có đang sử dụng Python 3.x không? Tôi sẽ đăng một bản cập nhật sẽ hoạt động với điều đó.
RocketDonkey

31

Lưu ý: Câu trả lời này không trả lời rõ ràng câu hỏi được hỏi. các câu trả lời khác làm điều đó. Vì câu hỏi dành riêng cho một tình huốngngoại lệ nêu ra là chung chung , nên câu trả lời này chỉ ra trường hợp chung.

Giá trị băm chỉ là số nguyên được sử dụng để so sánh các khóa từ điển trong quá trình tra cứu từ điển một cách nhanh chóng.

Bên trong, hash()phương thức gọi __hash__()phương thức của một đối tượng được đặt theo mặc định cho bất kỳ đối tượng nào.

Chuyển đổi danh sách lồng nhau thành một tập hợp

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

Điều này xảy ra do danh sách bên trong danh sách là danh sách không thể băm. Điều này có thể được giải quyết bằng cách chuyển đổi các danh sách lồng nhau bên trong thành một bộ ,

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

Băm rõ ràng một danh sách lồng nhau

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

Giải pháp để tránh lỗi này là cơ cấu lại danh sách để có các bộ giá trị lồng nhau thay vì danh sách.


4
Nếu danh sách quá lớn thì sao ?? vẻ đó là một giải pháp tốt nhưng không đủ chung
msh855

1
@ msh855 có giới hạn kích thước nào không? Tôi đã thử nghiệm từ điển với một bộ kích thước 100.000 và nó hoạt động tốt đối với tôi (tôi đang sử dụng python 3.6)
Sreram

18

Bạn đang cố gắng sử dụng k(là một danh sách) làm khóa cho d. Danh sách có thể thay đổi và không thể được sử dụng làm khóa chính.

Ngoài ra, bạn sẽ không bao giờ khởi tạo các danh sách trong từ điển, vì dòng này:

if k not in d == False:

Cần được:

if k not in d == True:

Thực tế phải là:

if k not in d:

5

Lý do bạn nhận được unhashable type: 'list'ngoại lệ là vì được k = list[0:j]đặt kthành một "phần" của danh sách, về mặt logic là một danh sách khác, thường ngắn hơn. Những gì bạn cần là chỉ lấy mục đầu tiên trong danh sách, được viết như vậy k = list[0]. Điều này cũng cho v = list[j + 1:]đó chỉ nên v = list[2]cho các phần tử thứ ba của danh sách trở về từ cuộc gọi đếnreadline.split(" ") .

Tôi nhận thấy một số vấn đề có thể xảy ra khác với mã, trong số đó tôi sẽ đề cập đến một số vấn đề. Một cái lớn là bạn không muốn (lại) khởi tạo dvới d = {}cho mỗi dòng đọc trong vòng lặp. Một ý kiến ​​khác là thường không nên đặt tên cho các biến giống với bất kỳ kiểu tích hợp nào bởi vì nó sẽ ngăn bạn truy cập vào một trong số chúng nếu bạn cần - và nó gây nhầm lẫn cho những người khác đã quen với tên chỉ định một trong những mục tiêu chuẩn này. Vì lý do đó, bạn nên đổi tên biến biến của mình listkhác đi để tránh các vấn đề như vậy.

Đây là phiên bản làm việc của bạn với những thay đổi này, tôi cũng đã đơn giản hóa ifbiểu thức câu lệnh mà bạn có để kiểm tra xem khóa đã có trong từ điển hay chưa - thậm chí còn có những cách ngầm hiểu ngắn hơn để thực hiện loại điều này, nhưng sử dụng điều kiện tuyên bố là tốt cho bây giờ.

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

Đầu ra:

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

0

Điều TypeErrornày đang xảy ra vì klà một danh sách, vì nó được tạo bằng cách sử dụng một lát cắt từ một danh sách khác với dòng k = list[0:j]. Điều này có lẽ phải là một cái gì đó giống nhưk = ' '.join(list[0:j]) , vì vậy bạn có một chuỗi thay thế.

Ngoài ra, iftuyên bố của bạn không chính xác như được ghi nhận bởi câu trả lời của Jesse, bạn nên đọc if k not in dhoặcif not k in d (tôi thích câu sau hơn).

Bạn cũng đang xóa từ điển của mình trên mỗi lần lặp vì bạn có d = {}bên trong forvòng lặp của mình .

Lưu ý rằng bạn cũng không nên sử dụng listhoặc filedưới dạng tên biến, vì bạn sẽ ẩn nội trang.

Đây là cách tôi viết lại mã của bạn:

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

Các dict.setdefault()phương pháp trên thay thế if k not in dlogic từ mã của bạn.


trong khi ưu tiên là toàn quyền của bạn, not k in dcó thể nhầm lẫn giữa một người mới như (not k) in d, trong khi k not in dkhông có sự mơ hồ
Jesse Game

Tôi thậm chí sẽ tranh luận rằng đó là cách 'pythonic' như not inđược liệt kê như một toán tử .
Jesse the Game

Vâng, tôi nghĩ sở thích của tôi có lẽ đến từ việc học các ngôn ngữ khác trước, trong đó đối với một thứ gì đó như bài kiểm tra ngăn chặn, bạn sẽ không có toán tử cho việc này, vì vậy bạn sẽ làm điều gì đó giống như vậy !a.contains(b). not incó thể khó hiểu hơn, tôi chỉ thấy khái niệm về hai toán tử từ khó hiểu hơn là sử dụng nghịch đảo trên biểu thức 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.