Python中的可哈希与不可哈希,对类做字典和集合去重
2020-09-13 本文已影响0人
xiaogp
摘要:id
,可变对象
,不可变对象
,hash
结论
- 可哈希的数据类型,即不可变的数据结构(数字类型(
int
,float
,bool
)字符串str
、元组tuple
、自定义类的对象
(需要修改__eq__
和__hash__
方法)) - 不可哈希的数据类型,即可变的数据结构 (
字典dict
,列表list
,集合set
) - 可哈希对象一定要是
不可变
对象
不可变类型,不同的值意味着不同的内存
,相同的值存储在相同的内存
,将的不可变对象理解成哈希表中的Key,将内存理解为经过哈希运算的哈希值Value,满足哈希表的性质
# 可哈希的不不可变对象
# 相同的值内存地址一致
a = 1
b = 1
c = 2
id(a) # 1399483872
id(b) # 1399483872
id(c) # 1399483904
对于可变对象,比如一个列表,更改列表的值,但是对象的地址本身是不变的
,不同的列表对象映射到了相同的地址,不符合哈希值的特性的,出现了哈希运算里面的冲突。
# 不可哈希的可变对象
# 对一个list做append操作改变list的值但是id内存地址不变
a = [1, 2]
id(a) # 1962260509064,每次都会变
a.append(3)
id(a) # 1962260509064
c = [1, 2]
id(c) # 1962260454728
在set,dict结构中必须使用可哈希对象
dict通过key的内存地址找值,如果用可变对象比如list作为dict的key,每次新建的list虽然值一样但是内存地址id都会改变
,导致实际找不到对应的value,Set使用的时HashSet同理也无法使用。
错误的写法,会报错TypeError: unhashable type: 'list'
set([[1, 2], [1, 2]])
{[1, 2]: 2}
正确的写法将可变对象转化为Tuple
set([(1, 2), (1, 2)])
{(1, 2): 2}
自定义对象是不可变对象,直接使用在set中或者dict中使用不会报错,但是依然无法使用
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def get_name(self):
return self.name
def get_age(self):
return self.age
调用set去重无效,a和c的id地址不一致
a = Student("gp", 12)
b = Student("gp", 21)
c = Student("gp", 12)
{<__main__.Student at 0x1c8dfdcbe10>,
<__main__.Student at 0x1c8dfdcbeb8>,
<__main__.Student at 0x1c8dfdcbef0>}
调用dict无效,找不到key,新建的对象和a的id地址不一致
tmp = {a:1}
tmp[Student("gp", 12)]
TypeError: unhashable type: 'Student'
需要重写了类的__eq__
和__hash__
方法,前者可以使用==
判断类是否相等,后者可以用来做dict的key和set去重
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def get_name(self):
return self.name
def get_age(self):
return self.age
def __eq__(self, other):
if all([self.name == other.name, self.age == other.age]):
return True
return False
def __hash__(self):
return hash(self.name + str(self.age))
a = Student("gp", 12)
b = Student("gp", 21)
c = Student("gp", 12)
a == c # True
a.__hash__() == c.__hash__() # True
id(a) == id(c) # False
set([a, b, c]) # {<__main__.Student at 0x1c8dfd6a5f8>, <__main__.Student at 0x1c8dfd74780>}
tmp = {a:1}
tmp[Student("gp", 12)] # 1
for item in set([a, b, c]):
print(item.get_name())
# gp
# gp