使用set对python中循环的优化

set定义

日常项目中大家或多或少都使用到集合。先走走流程,介绍下set的定义:
集合(set)是一个无序的不重复元素序列。可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。

项目中常用去重手段

集合的内置方法也有很多,常用就是 使用add,remove。然后我们项目中经常会使用set进行一个去重的操作如:
a = [1,2,3,3,4,4,5,6]
print(list(set(a)))

>[1, 2, 3, 4, 5, 6]

使用set优化场景及方法

项目中偶尔会遇到这个情况,有的会这么写:
假设a表示所有员工,
b表示参加比赛的员工,
要找出没参加比赛的员工。

a = [i for i in range(20)]
b = [i for i in range(5,15)]
result = []
for j in a:
    if j not in b:
        result.append(j)
print(result)

>[0, 1, 2, 3, 4, 15, 16, 17, 18, 19]

这种可以用到集合中求差集的方式 即:

a = [i for i in range(20)]
b = [i for i in range(5,15)]
print(list(set(a)-set(b)))

>[0, 1, 2, 3, 4, 15, 16, 17, 18, 19]

我们来大致比较下两个执行时间

from time import time
t = time()
a = [i for i in range(20)]
b = [i for i in range(5,15)]
result = []
for i in range(1000000):
    for j in a:
        if j not in b:
            result.append(j)
print(time() - t)
t = time()
for i in range(1000000):
    list(set(a)-set(b))
print(time() - t)

>3.550997734069824
>1.3410418033599854

可以看到时间上是2倍多接近3倍的差距。
当然稍微扩大点 a,b 的数据,
最后是4倍左右的差距。
数据可能不是很精准,但是起码可以证明遇到类似问题可以用这个方法。

使用set的类似场景

假设a表示所有会后端员工,
b表示所有会前端员工,
如果 要找出 前后端都会的员工:

list(set(a) & set(b))

如果 要找出技术员工

list(set(a) | set(b))

set去重原理

原理: set去重是调用了内部的__hash__方法 和 __eq__方法
1、当两个变量的哈希值不相同时,就认为这两个变量是不同的
2、当两个变量哈希值一样时,调用__eq__方法,当返回值为True时认为这两个变量是同一个,应该去除一个。返回FALSE时,不去重

class Mystring:
    def __init__(self,string):
        self.string = string
    def __hash__(self):
        result = hash(id(self))
        print("%s调用了哈希方法,哈希值为:%s"%(self.string, result))
        return result
    def __eq__(self, other):
        print("%s调用了eq方法"%self)
        return self.__dict__ == other.__dict__
    def __repr__(self):
        return self.string
f1 = Mystring('aaa')
f2 = Mystring('aaa')
f3 = Mystring('ccc')
ls = [f1,f2,f3]
print(set(ls))

>
aaa调用了哈希方法,哈希值为:2616513427440
aaa调用了哈希方法,哈希值为:2616513428784
ccc调用了哈希方法,哈希值为:2616513429120
{aaa, aaa, ccc}

这里的结果不是{aaa,ccc} ,其实看哈希值其实就知道了。
原因很简单,我们是创建了f1,f2两个对象实例,这两个对象实例当然不一样了,
要实现去重可以这么实现,改下hash的方法

def __hash__(self):
        result = hash(self.string))
        print("%s调用了哈希方法,哈希值为:%s"%(self.string, result))
        return result

再次执行的结果如下:

aaa调用了哈希方法,哈希值为:-8445055340194366239
aaa调用了哈希方法,哈希值为:-8445055340194366239
aaa调用了eq方法
ccc调用了哈希方法,哈希值为:6109596974613880257
{aaa, ccc}

这里理解到了话。应用场景可以如下:
对对象进行去重,
例如 员工表中有重复数据,
字段如下: 姓名,年龄,部门,手机号。
若只认为姓名和年龄相同就是一个人,
求全部员工。

那么在hash的时候就只hash 姓名和年龄, 把eq,写死为True即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值