Python 基础系列 5 - Set 去重原理

在上篇文章《哈希值和可变性Hash value and mutability》最后说到 set 去重问题,所以这篇主要是通过实践来研究一下 set 去重背后的故事,当然也是参考了网上一些资料得到了一些启发,感谢那些陌生的喜欢分享的博友们。想了解更多 Python 基础序列文章,请参考 Python 基础系列大纲

set 的简单应用
利用 set 中元素的唯一性,我们可以对 list 去重

list1 = [1,2,3,1,2,3]
print(set(list1))
# output: {1, 2, 3}

set 去重的原理

  1. set 中元素的 hash 值不一样
class Person:
    def __init__(self, name, identityId):
        self.name = name
        self.identityId = identityId
        
    def __hash__(self):
        print("%s call hash method"%self.name)
        return hash(id(self))
    
    def __eq__(self, other):
        print("%s call eq method"%self.name)
        if self.__dict__ == other.__dict__:
            return True
        else:
            return False
                
p1 = Person('p1', 123456789)
p2 = Person('p2', 123456789)
print("p1 id: %s"%hex(id(p1)))
print("p2 id: %s"%hex(id(p2)))
list_test = [p1,p2]
print(set(list_test))

可以看出 set 调用了元素的 hash 方法,p1 和 p2 的 hash 返回不同,就认为是不重复的元素,所以不去重

#output:
p1 id: 0x209563fabe0
p2 id: 0x209563fa910
p1 call hash method
p2 call hash method
{<__main__.Person object at 0x00000209563FABE0>, <__main__.Person object at 0x00000209563FA910>}
  1. set 中元素的 hash 返回值是一样的
class Person:
    def __init__(self, instance, name, identityId):
        self.instance = instance
        self.name = name
        self.identityId = identityId
        
    def __hash__(self):
        print("%s call hash method"%self.instance)
        return hash(self.identityId)
    
    def __eq__(self, other):
        print(f"{self.instance} call eq method: equal to {other.instance}")
        if self.name == other.name:
            return True
        else:
            return False
    
p1 = Person('p1','kelly', 123456789)
p2 = Person('p2','xia', 123456789)
p3 = Person('p3','peter', 111111111)
p4 = Person('p4','kelly', 123456789)
p5 = Person('p5','kelly.xia', 123456789)
print("p1 id: %s"%hex(id(p1)))
print("p2 id: %s"%hex(id(p2)))
print("p3 id: %s"%hex(id(p3)))
print("p4 id: %s"%hex(id(p4)))
print("p5 id: %s"%hex(id(p5)))
print(f"p1==p4:{p1==p4}")
list_test = [p1,p2,p3,p4,p5]
print(set(list_test))

p1, p2, p3, p4, p5,通过 id 来看是指向不同的引用的,p1 和 p2 的 hash 返回值相同,所以再调用 p1 的 eq 方法,eq 返回是 False 的,所以认为 p1 和 p2 是不重复的。而 p1 和 p4,hash 返回值是一样的,再调用 p1 的 eq 方法,返回是 Ture,所以认为 p1 和 p4 是重复的,将去除 p4.最后的 p5,跟 p1,p2 的 hash 返回值都是一样的,所以再分别调用 p1 和 p2 的 eq 方法,因为 eq 访求返回都为 False,所以认为 p5 分别和 p1,p2 是不重复的。

#output:
p1 id: 0x209564e1fd0
p2 id: 0x209564e1070
p3 id: 0x209564e1ac0
p4 id: 0x209564e1430
p5 id: 0x209564e1c40
p1 call eq method: equal to p4
p1==p4:True
p1 call hash method
p2 call hash method
p1 call eq method: equal to p2
p3 call hash method
p4 call hash method
p1 call eq method: equal to p4
p5 call hash method
p1 call eq method: equal to p5
p2 call eq method: equal to p5
{<__main__.Person object at 0x00000209564E1070>, <__main__.Person object at 0x00000209564E1FD0>, <__main__.Person object at 0x00000209564E1C40>, <__main__.Person object at 0x00000209564E1AC0>}

结论
由以上得出结论:set 的去重是通过两个函数__hash__和__eq__结合实现的。当两个对象的哈希值不相同时,就认为这两个对象是不同的; 当两个对象的哈希值一样时,调用__eq__方法,当返回值为 True 时认为这两个对象是重复的,应该去除一个。返回 FALSE 时,认为这两个对象不是重复的,所以不去重。

应用
利用 set 去重特性,我们扩展思维应用到许多需要去重的场景中去,关键就是 override 超类 Object 的__hash__和__eq__方法。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值