在Python中如何检查两个对象是否已改变?
我有一个对象,它的结构如下:
class Obj1(object):
def __init__(self, user, password, items=None):
self._user = user
self._password = password
self._items = items
def add_items(self, item):
self._items.append(item)
def has_changed(self, obj2):
return self != obj2
现在我做了以下操作:
obj1 = Obj1('me', '1234')
obj1.add_item({'name':'george', 'progress':'70'})
#obj2 = obj1 #wont work since they would point to same object
obj2 = copy.copy(obj1)
obj1.add_item({'name':'monica', 'progress':'86'})
print obj2.has_changed(obj1)
令人惊讶的是,这个操作返回了false。有人能告诉我我漏掉了什么吗?
2 个回答
这是你代码的一个可运行版本。我添加了需要的copy
模块导入,并加上了冒号,让代码变得有效。
import copy
class Obj1(object):
def __init__(self, user, password, items=None):
self._user = user
self._password = password
self._items = [] if items==None else items
def add_item(self, item):
self._items.append(item)
def has_changed(self, obj2):
return self != obj2
obj1 = Obj1('me', '1234')
obj1.add_item({'name':'george', 'progress':'70'})
#obj2 = obj1 #wont work since they would point to same object
obj2 = copy.copy(obj1)
obj1.add_item({'name':'monica', 'progress':'86'})
print(obj2.has_changed(obj1))
print(obj1.has_changed(obj1))
这个看起来可以工作,但实际上并不完全如此(见下文)。注意,我又添加了一个测试,以确保在比较时,无论是True还是False都能正常工作……但到目前为止,测试的效果还不够好。
不过,你应该看看@Viktor的回答,因为那里面解释了对象相等性检查(你在类中继承的部分)并不是检查任何属性值是否相等,而只是检查这两个对象是否是同一个。
你可以重写对象的 __eq__
方法。当你比较对象时,实际上是比较它们的身份(也就是说,它们不是同一个对象,所以用 ==
比较会返回 False
):
用户自定义的类默认有
__eq__()
和__hash__()
方法;有了这些方法,所有对象之间的比较结果都是不相等的(除了和它们自己比较),而且x.__hash__()
返回的是id(x)
。
这里有个简单的例子:
>>> class A(object):
... def __init__(self, i):
... self.i = i
...
>>>
>>> a = A(1)
>>> b = A(1)
>>> c = A(2)
>>> a == b
False
>>> a == c
False
但是如果你重写了比较方法,就能得到你想要的结果:
>>> class B(object):
... def __init__(self,i):
... self.i = i
... def __eq__(self,o):
... return self.i == o.i
... def __ne__(self,o):
... return self.i != o.i
...
>>> d = B(1)
>>> e = B(1)
>>> f = B(2)
>>> d == e
True
>>> d == f
False
>>>
另外,比较目录时会进行“深度比较”(所以你可以直接比较字典):
>>> d1 = {1:2, 3:4}
>>> d2 = {}
>>> d2[1] = 2
>>> d2[3] = 4
>>> d3 = {5:6, 3:4}
>>> d1 == d2
True
>>> d1 == d3
False
还要注意,在实现 丰富比较 方法时,有一些规则[1][2] 需要遵循,例如:
比较运算符之间没有隐含关系。
x==y
的真实性并不意味着x!=y
是假的。因此,在定义__eq__()
时,也应该定义__ne__()
,这样运算符的行为才会符合预期。丰富比较方法的参数永远不会被强制转换。
如果一个类重写了
__eq__()
但没有定义__hash__()
,那么它的__hash__()
会被隐式设置为None
。
请求更新:
参数永远不会被强制转换(强制转换 在 Python 词汇表中)意味着检查输入参数(在我的例子中是 o
)是你的责任,可以尝试:
>>> d == 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __eq__
AttributeError: 'int' object has no attribute 'i'
而且不同类的对象之间也可以进行比较:
>>> d == a
True
关于 __hash__
被设置为 None
的意思是,hash(obj)
会失败:
>>> hash(d)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'B'
而且每个需要哈希的集合也会失败:
>>> set((d,))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'B'
而对于 A
来说是可以正常工作的:
>>> set((a,))
{<__main__.A object at 0x7f8a85fe4668>}