Python学习

Python中的可哈希与不可哈希,对类做字典和集合去重

2020-09-13  本文已影响0人  xiaogp

摘要:id可变对象不可变对象hash

结论


不可变类型,不同的值意味着不同的内存相同的值存储在相同的内存,将的不可变对象理解成哈希表中的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
上一篇 下一篇

猜你喜欢

热点阅读