魔术方法(续)
让魔法继续
首先,你得明白Python中 is
和==
的区别。
Python中的比较 is 和 ==
-
is
: 比较两个对象的 id 值是否相等,是否指向同一个内存地址; -
==
: 比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了。
l1 = [1, 2, 3]
l2 = [1, 2, 3]
print(l1 == l2)
print(l1 is l2)
输出:
#l1和l2列表里的内容一致
True
#l1和l2指向的内存地址是不一样的
False
使用上节中的Person来试试:
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
p1 = Person('Pie', 10)
p2 = Person('Pie', 10)
print(p1 == p2)
print(p1 is p2)
输出:
False
False
虽然p1和p2的内容都一样,但是当调用 ==
为啥还是 False ?
在此,我们需要学习一个非常重要的魔法方法 __eq__
。
__eq__方法
因为Person是一个 类
,对于 自定义类而言,你必须自己实现 ==
的操作逻辑,例如你拿到两人的资料,你如何判断两人是不是同一个人?
你会先比较姓名是否一致,接着比较身高,体重等等各方面参数。
它们在比较时候,调用的是对象中的__eq__方法比较,其默认比较的是内存地址。 如果要更改比较方式,则需要在__eq__方法中修改一下,如下代码所示:
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.__dict__ == other.__dict__
p1 = Person('Pie', 10)
p2 = Person('Pie', 10)
print(p1 == p2)
print(p1 is p2)
输出:
True
False
__hash__方法
哈希(hash)也翻译作散列。Hash算法,是将一个不定长的输入,通过哈希函数变换成一个定长的输出,即哈希值。Hash主要应用在数据结构以及密码学领域。 例如:
print(hash('Pie'))
每次运行产生的hash值是不一样的,相同字符串在同一次运行时的哈希值是相同的,但是不同次运行的哈希值不同。
这是由于Python的字符串hash算法有一个启动时随机生成secret prefix/suffix
的机制,存在随机化现象:对同一个字符串输入,不同解释器进程得到的hash结果可能不同。
因此当需要做可重现可跨进程保持一致性的hash
,需要用到hashlib
模块。
使用hashlib模块
import hashlib
md5 = hashlib.md5() # 应用MD5算法
data='Pie'
md5.update(data.encode('utf-8'))
print(md5.hexdigest())
输出:
e2503aa44f0d264d69e75db773855db1
实现__eq__()
和__hash__()
方法
在自定义类中,如果没有实现__eq__()
和__hash__()
方法,会继承object的__eq__()方法和__hash__()方法。
hash()
函数默认调用Object类的__hash__()
方法。Hash值是对象id值1/16
。
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
p1 = Person('Pie', 18)
print(id(p1))
print(hash(p1))
自定义对象添加到集合中,我们一般认为两个对象的属性值相同就是同一个对象。因此需要我们手动复写__eq__
方法和__hash__
方法。
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.__dict__ == other.__dict__
def __hash__(self):
return hash(self.name) + hash(self.age)
p1 = Person('Pie', 18)
p2 = Person('Pie', 18)
set = {p1, p2}
print(len(set))
输出:
1
例如你拿到两人的资料,你如何判断两人是不是同一个人?如果不是同一个人就把两人的信息放到资料夹中。
你还是会先比较姓名是否一致,还是接着比较身高,体重等等各方面参数,当然,你要是有两人的DNA信息,自然就可以只用DNA信息获得结论。
不恰当类比,对于Person来说,__hash__
方法就可以看做是一个人的DNA信息。
上例中,我们使用name
和age
的哈希值相加的方式来产生hash值。
小结
本章介绍__eq__()
和__hash__()
魔术方法,相关概念非常重要,是你应该熟练掌握的。
在我们面向对象编程的时候,会需要大量的实现这2个魔法方法。
毕竟,大千世界,各有不同。