使用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即可。