跳转至

魔术方法(续)

让魔法继续

首先,你得明白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信息。

上例中,我们使用nameage的哈希值相加的方式来产生hash值。

小结

本章介绍__eq__()__hash__()魔术方法,相关概念非常重要,是你应该熟练掌握的。 在我们面向对象编程的时候,会需要大量的实现这2个魔法方法。

毕竟,大千世界,各有不同。