Python:TypeError:无法散列的类型:“列表”


94

我正在尝试拍摄一个看起来像这样的文件

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'。我知道字典中的键不能是列表,但是我试图将我的值变成列表而不是键。我想知道我是否在某个地方犯了一个错误。

Answers:


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在行“ line = line.split('x')”上不支持缓冲区API
Keenan

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

1
@ msh855有大小限制吗?我用一个大小为100,000的元组测试了字典,它对我来说很好用(我使用python 3.6)
Sreram

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

我注意到了代码的其他一些可能的问题,我将提及其中的一些问题。一个大的一个是你不希望(重新)初始化dd = {}每一行的循环中读取。另一个是,将变量命名为任何内置类型通常不是一个好主意,因为它会阻止您在需要时访问其中一个变量,并且会使习惯于该变量的其他人感到困惑。指定这些标准项目之一的名称。因此,您应该将变量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是因为是一个列表,因为它是使用另一个带有line的列表中的一个切片创建的k = list[0:j]。这可能类似于k = ' '.join(list[0:j]),因此您需要一个字符串。

除此之外,if正如Jesse的回答所指出的那样,您的陈述不正确,应该读为if k not in dif not k in d(我更喜欢后者)。

您还需要d = {}for循环中清除字典,因为每次迭代都在其中。

请注意,您也不应使用listfile作为变量名,因为您将掩盖内建函数。

这是我重写代码的方法:

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

我什至认为这是not in被列为运算符的“ pythonic”方式。
Jesse the Game

是的,我想我的偏好可能首先来自学习​​其他语言,在这种情况下,对于像遏制测试这样的操作,您将没有操作符,因此您会做类似的事情!a.contains(b)not in也许是更pythonic的,我只是发现两个单词运算符的概念比在布尔表达式上使用逆运算更令人困惑。
安德鲁·克拉克

-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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.