A Brief Talk of "Equality"

Well, I mean, Python Object Equality

Posted by Wang Zhihao on 2016-11-09

今天在写unittest的时候发现了一个关于「Python Object Equality」的问题,简化的问题模型如下:

class Message(object):
def __init__(self, id, content):
self.id = id
self.content = content
def is_equal(obj1, obj2):
if obj1 == obj2:
print 'equal'
else:
print 'not equal'
msg_a = Message(1, 'This is a msg')
msg_b = Message(1, 'This is a msg')

输出结果是这样的:

not equal

如果我们重写__eq__

class Message(object):
def __init__(self, id, content):
self.id = id
self.content = content
def __eq__(self, other):
return self.id == other.id and self.content == other.content
def is_equal(obj1, obj2):
if obj1 == obj2:
print 'equal'
else:
print 'not equal'
msg_a = Message(1, 'This is a msg')
msg_b = Message(1, 'This is a msg')
is_equal(msg_a, msg_b)

输出结果就变成了这样:

equal

== vs is

  • == tests whether two objects have the same value
  • is tests whether two objects are the same object

但其实这两句话并不能完全概括==is的区别,比如运行下面这一段代码:

def find_max():
x = 1
y = 1
while(x is y):
x = x + 1
y = y + 1
print "max: %s" % (x - 1)
find_max()

你猜结果是多少?

文末公布答案。

之前做了一些关于Python的is==的小小的实验,之后会整理并总结一下。在这里先不做更多的叙述了。

重载 __eq__ 方法

==is有一个这样的区别:

== can be overloaded, but is cannot.

Both the __eq__ and __cmp__ special methods allow a class to decide for itself what equality means.

Because those methods are regular Python code, they can do anything. An object might not be equal to itself. It might be equal to everything. It might randomly decide whether to be equal or not. It might return True for both == and !=.

一个对象可能不等于它自己。
一个对象可能等于一切对象。

总而言之,每一个类都可以去定义自己的相等的概念。
当然了,也可以自己定义不相等的概念——你可以去重写__ne__方法。

当 Python 遇见 a == b

  • 如果 type(b) 是新式类,且 type(b)type(a)的子类,且 type(b)__eq__被重写了,那么结果就是 b.__eq__(a)

  • 如果 type(a)__eq__ 被重载了(也就是说 type(a).__eq__不再是 object.__eq__),那么结果就是 a.__eq__(b)

  • 如果 type(b)__eq__ 被重载了,那么结果是 b.__eq__(a)

  • 如果不是上面的情况,Python会不断地请求 __cmp__ 方法。如果__cmp__ 方法存在:

    • 两个对象是相等的 当且仅当 返回值为0
  • 如果不是上面的情况,最终,Python会调用 object.__eq__(a, b)

    • 两个对象是相等的 当且仅当 返回值为True

要注意的是:如果ab都没有重载 ==, 那么 a == ba is b 是一样的。

It is not the end…

def find_max():
x = 1
y = 1
while(x is y):
x = x + 1
y = y + 1
print "max: %s" % (x - 1)
find_max()

结果如下:

max: 256

2的8次方

恩,关于这个话题其实能说的还有太多太多,之后可以再进行更多深入的讨论。

Equality matters.

Reference