天天看點

Python 的 unhashable type 錯誤分析及解決錯誤案例分析

沒錯,玩自動化測試時,又報錯了。

Python 的 unhashable type 錯誤分析及解決錯誤案例分析

日常測試中,經常會使用py的 set 和 dict,set 是用 dict 實作,因為本身 dict 的 key 就是會被去重,value 設定為 None 即可作為 set 使用。

Python 中的 dict 内部使用了哈希表的方式實作,是以對于 key 的要求就是需要計算哈希值。在 Python 的類型體系中,有些類型是支援計算哈希值,有些并不支援。是以我們可以知道,使用不支援計算哈希值的類型作為 dict 或 set 的 key 就會報錯。

Python 的 unhashable type 錯誤分析及解決錯誤案例分析

錯誤案例

以下皆報錯

TypeError: unhashable type: 'list'

# list 作為 dict 的 key
key = ["news", "hot"]
news = {}
news[key] = ["news_1", "news_2"]

# list 作為 set 的 key 來去重
categories = [["news", "hot"], ["sports", "nba"]]
categories = set(categories)      

分析

我們現在知道了這個錯誤的原因,那麼 Python 内置類型中哪些支援哈希計算,哪些不支援了。

下面我們測試一下 Python 内置的類型。

import sys


def check_hash(x):
    if x.__hash__ is not None:
        print type(x), 'hashable:', hash(x)
        return True
    else:
        print type(x), 'unhashable'
        return False

# int
i = 5
check_hash(i)
# long
l = sys.maxint + 1
check_hash(l)
# float
f = 0.5
check_hash(f)
# string
s = "hello"
check_hash(s)
# unicode
u = u"中國"
check_hash(u)
# tuple
t = (i, l, f, s, u)
check_hash(t)
# object
o = object()
check_hash(o)

# list
l1 = [i, l, f, s, u]
check_hash(l1)
# set
s1 = {i, l, f, s, u}
check_hash(s1)
# dict
d1 = {s: i, u: l}
check_hash(d1)

# output:
<type 'int'> hashable: 5
<type 'long'> hashable: -9223372036854775808
<type 'float'> hashable: 1073741824
<type 'str'> hashable: 840651671246116861
<type 'unicode'> hashable: 2561679356228032696
<type 'tuple'> hashable: 1778989336750665947
<type 'object'> hashable: 270043150
<type 'list'> unhashable
<type 'set'> unhashable
<type 'dict'> unhashable      

set、list、dict 三個類型是不可哈希的。對于可變的類型計算哈希值是不可靠的,當資料發生變化時哈希值也要變化。哈希計算的意義在于用哈希值來區分變量,哈希值會随着變量内容而變化,是以對于這類可變類型來說,不支援哈希值是合理的。

下面介紹下上述示例代碼的一些細節,對于 Python 的深入了解有一定幫助。

定義 set

定義 set 的方法,這裡需要單獨說一下。set 有多種定義的方法,一般使用 set(list) 或 set(tuple) 的方式來定義,但是還有個花括号的方法可以定義,這個大家使用的較少會被忽略,就是上述示例中的方式。

l = ['a', 'b', 'a', 'c']
s = set(l)

# 使用花括号來定義
s = {'a', 'b', 'a', 'c'}      

參考

http://icejoywoo.github.io/2019/03/16/python-unhashable-type-error.html