python中的集合之所以能去重是因为集合中实现了两个方法__hash__和__eq__。
对于这两个方法的详细介绍可以看官方文档
object.hash(self)
Called by built-in function hash() and for operations on members of hashed collections including set, frozenset, and dict.
上面hash()方法和3个基本类型中都实现了__hash__()。
If a class does not define an eq() method it should not define a hash()operation either; if it defines eq() but not hash(), its instances will not be usable as items in hashable collections. If a class defines mutable objects and implements an eq() method, it should not implement hash(), since the implementation of hashable collections requires that a key’s hash value is immutable (if the object’s hash value changes, it will be in the wrong hash bucket).
当class没有定义__eq__()方法时,那么它也不应该定义__hash__()方法。如果它定义了__eq__()方法,却没有定义__hash__()方法,那么这个类的实例就不能在可哈希集合使用。如果一个类定义了一个可变对象(这里应该是指class的成员之一为可变对象),且implement了__eq__()方法,那么这个类就不应该implement hash()方法,因为可哈希对象的实现(implement )要求键值key的hash值是不变的(如果一个对象的hash值改变了,那么它会被放在错误的hash桶里)
下面代码中会用到__dict__方法,我们这里提前说一下它,类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里的,对象的__dict__中存储了一些self.xxx的一些东西。打印对象的__dict__图片如下。
{'a': 0, '__module__': '__main__', 'b': 1, 'class_test': <classmethod object at 0x00000000021882E8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__init__': <function __init__ at 0x00000000023A5BA8>, 'test': <function test at 0x00000000023A5C18>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': '\n Class A.\n ', 'static_test': <staticmethod object at 0x00000000021881C8>}
class Foo:
def __init__(self, nickname, count):
self.nickname = nickname
self.count = count
def __hash__(self):
print("%s调用了哈希方法" % self.nickname)
return hash(id(self))
def __eq__(self, other):
print("%s调用了eq方法")
if self.__dict__ == other.__dict__:
return True
else:
return False
f1 = Foo('f1', 1)
f2 = Foo('f2', 2)
f3 = Foo('f3', 3)
ms = [f1, f2, f3]
print(set(ms))
class Foo:
def __init__(self, nickname, count):
self.nickname= nickname
self.count = count
def __hash__(self):
print("%s调用了hash方法" % self.nickname)
return hash(self.count)
def __eq__(self, other):
print("%s调用了eq方法" %self.nickname)
return self.__dict__ == other.__dict__
f1 = Foo('f1', 1)
f2 = Foo('f2', 2)
f3 = Foo('f3', 1)
ms = [f1, f2, f3]
print(set(ms))
调用结果:
结论
set的去重是通过两个函数__hash__和__eq__结合实现的。
1、当两个变量的哈希值不相同时,就认为这两个变量是不同的。
2、当两个变量哈希值一样时,调用__eq__方法,比较是否具有相同的各种属性,可以简单地理解为值是否相等,但我们需要知道除了值以外,还比较了很多其他的属性。当返回值为True时认为这两个变量(对象)是同一个,应该去除一个。返回FALSE时,仅仅hash冲突,不是同一个对象,不去重。
PS:不准白PIAO,点完赞再走!