集合过滤机制


文章目录

  • 集合过滤机制
  • 简介
  • Set详解
  • Set使用
  • 补充说明


简介

  • 底层分析set的实现原理。
  • 详细分析Python的set容器如何过滤自定义类。
  • 代码实现多熟悉自定义类的按照指定属性使用set进行过滤。

Set详解

  • Python的set容器又叫可哈希的集合,核心算法为hash算法。其原理可以理解为有多个桶,每个桶是一个数组类型的容器,通过hash值快速定位在哪个桶,通过eq方法比较是否已经存在该对象在桶内,不在则放入该桶(注意判断是否存在该桶对象默认eq方法比较的是对象的id值)。
  • 主要流程如下。
  • 调用对象的__hash__方法计算对象的hash值,确定该对象放到哪个桶中。
  • 调用对象的__eq__方法对比桶内对象是否和当前对象相等,不等则添加进去。

Set使用

  • 基础使用
s = set()
s.add(1)
s.add(2)
s.add(3)
s.add(1)
print(s)
  • 可以看到,对基础数据类型,直接对值取hash值,对值进行eq(=)判断,可以很方便使用set进行过滤。
  • 自定义类
  • __eq__方法
class Foo(object):
      def __init__(self, a, b):
          self.a = a
          self.b = b
      
      def __eq__(self, other):
          return self.a == other.a and self.b == other.b

  obj1 = Foo(1, 2)
  obj2 = Foo(1, 2)
  print(obj1 == obj2)
  print(obj1 is obj2)
  • 这里重写了相等判断的方法,"=="运算符的结果就是调用这个方法得到结果。但是,"is"运算符的结果是比较两个对象的id(可以理解为物理地址的映射值),不同的对象在内存中地址一定不同,id值也就不同。
  • __hash__方法
class Foo(object):
      def __init__(self, a, b):
          self.a = a
          self.b = b
      
      def __eq__(self, other):
          return self.a == other.a and self.b == other.b

  obj1 = Foo(1, 2)
  obj2 = Foo(1, 2)
  obj3 = Foo(2, 1)
  s = set()
  s.add(obj1)
  s.add(obj2)
  s.add(obj3)
  • 执行上面这段代码会报错TypeError: unhashable type: 'Foo',这是因为自定义的类没有实现__hash__方法,无法得到对象的hash值就不能确定“桶”的位置。
  • 完整实现
class Foo(object):
      def __init__(self, a, b):
          self.a = a
          self.b = b
      
      def __eq__(self, other):
          return self.a == other.a and self.b == other.b
      
      def __hash__(self):
          return hash(self.a + self.b)

  obj1 = Foo(1, 2)
  obj2 = Foo(1, 2)
  obj3 = Foo(2, 1)
  s = set()
  s.add(obj1)
  s.add(obj2)
  s.add(obj3)
  for item in s:
      print("a:", item.a, "b:", item.b)
  • 这里重写了__hash__和__eq__方法,注意,为了区分开不同对象,hash取值的属性一定要合理,要以自己的需求为目的(这里只有a和b分别相同就认为是“同一个对象”)。对a和b的和取hash值,和相同的在一个桶,进而判断是否eq所需方法相等。显然,obj2被过滤掉了。

补充说明

  • 本脚本运行环境为Python3.6,要求Python版本必须达到Python3.4以上。
  • 这里只是进行了运行机制的解释,底层实现较为繁琐,不做叙述。
  • 实现自定义类的对象按照部分属性利用set过滤,如有错误,欢迎指正。