简单谈谈Python中的dict{}与set()与__eq__、__hash__的关系

1:预备知识(Hash)
1.1:在Python3中,dict和set数据结构要求键值key是可hash的,这样可以保证键值key的唯一性。简要的说可hash的数据类型就是不可变的数据结构(字符串str、元组tuple、对象集objects),它是一个将大体量数据转化为很小数据的过程,甚至可以仅仅是一个数字,以便我们可以用在固定的时间复杂度下查询它,所以,hash对高效的算法和数据结构很重要。这点和数字图像处理中经常用到的LUT(look-up-table)很像,感兴趣的可以百度一下。
1.2:不可哈希的类型有列表(list可以append和pop进行变化)、字典(dict可以添加key与value变化)、集合(set()具有add等方法)
这里简单说一下,使用Hash算法的数据结构叫做哈希表,也叫散列表,主要是为了提高查询的效率。他通过映射来访问自己想要的值,而Python3中的字典就是一个典型的哈希表。
1.3:Python3中使用 __hash__ 返回一个int值,用来唯一标记这个对象。

class test:
    def __init__(self,value):
        self.val = value
p1 = test(1)
p2 = test(2)
p3 = test(3)
print(set([p1,p2,p3]))

In [49]:runfile('C:/Users/16288/Desktop/ALBB.py', wdir='C:/Users/16288/Desktop')
{<__main__.test object at 0x0000022563BB04A8>, <__main__.test object at 0x0000022563BB01D0>, <__main__.test object at 0x0000022563BB0160>}

可以看到test类自动继承了__hash__ 函数,输出了值。
1.4:什么是__eq__与__hash__?
在官方文档中,它们俩定义如下:

    def __hash__(self):
        return hash(id(self))
    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return hash(id(self))==hash(id(other))
        else:
            return False          

首先考虑非类对象:

a = 2
b = 3
c = 4
d = 2
print(set([a,b,c,d]))

其结果为:
{2, 3, 4}
查看他们的hash,可以看到,其中a和d因为值一样,所以hash后的值也是一样的。
在这里插入图片描述
那么对于类对象呢?
做一个比喻,譬如你有1~N号盆,每个盆只能放一类球,又有m个类型的小球要放到盆里面。而__hash__作用就是判断第i个小球可以放到哪个盆里面,找到盆的文章,__eq__的作用就是在已经找到了盆的情况下,若这个盆已经有了一个小球,但又来了一个球,它声称它也应该装进这个盆里面(__hash__函数给它说了盆的位置),双方僵持不下,那就得用__eq__函数来判断这两个球是不是相等的(equal),如果是判断是相等的,那么后来那个球就不应该放进盆里,哈希集合维持现状。

class Foo:
    def __init__(self,name,count):
        self.name = name
        self.count = count
    def __hash__(self):
        print("%s调用了哈希方法"%self.name)
        return hash(self.count)
    def __eq__(self, other):
        print("%s调用了eq方法"%self.name)
        return self.__dict__==other.__dict__
f1 = Foo('f1',1)
f2 = Foo('f2',2)
f3 = Foo('f3',3)
ls = set([f1,f2,f3])
print(ls)
print('===================')
f4 = Foo('f4',3)
ls.add(f4)
print('f3的id:',id(f3))
print('f4的id:',id(f4))

结果如下:
在这里插入图片描述
其中count是key值,也就是需要hash的值,而name和count会在后面eq方法中的__dict__出现,可以看到,f3和f4的hash值是相等的,但是set并没有这么简单就判断f4,f3是重复的,而是继续调用了eq方法判断f3和f4是否相等,只有相等的时候才会认为是同一个。
为了验证上面的假设,我们可以执行以下输入:

f1 = Foo('f1',1)
f2 = Foo('f1',1)
f3 = Foo('f3',3)
ls = set([f1,f2,f3])
print(ls)

结果为:
在这里插入图片描述
很明显,这里只存了两个值,所以不难看出,f1和f2被set判断重复,只取了一个。
2:结论
set的去重是通过两个函数__hash__和__eq__结合实现的。
1、当两个变量的哈希值不相同时,就认为这两个变量是不同的
2、当两个变量哈希值一样时,调用__eq__方法,当返回值为True时认为这两个变量是同一个,应该去除一个。返回FALSE时,不去重
而dict直接采用的是hashmap的结构。
PS:实际上set可以看作是没有value的dict,请读者自行思考原因。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值