Reputation: 3941
I have an object that has the following layout:
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
Now I do the following:
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)
Surprisingly this returns me false. Can someone point me out what I am missing here?
Upvotes: 2
Views: 3586
Reputation: 20997
You may just override __eq__
method of the object. When you are just comparing objects only identities are compared (they are not the same object, thus ==
will result in False
):
User-defined classes have
__eq__()
and__hash__()
methods by default; with them, all objects compare unequal (except with themselves) andx.__hash__()
returnsid(x)
Here's a small example:
>>> 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
But if you override comparison you'll get what you need:
>>> 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
>>>
Also comparing directories does "deep comparison" (so you can compare dictionaries directly):
>>> d1 = {1:2, 3:4}
>>> d2 = {}
>>> d2[1] = 2
>>> d2[3] = 4
>>> d3 = {5:6, 3:4}
>>> d1 == d2
True
>>> d1 == d3
False
Also note that there are some rules[1][2] that you should follow when implementing rich comparison methods, for example:
There are no implied relationships among the comparison operators. The truth of
x==y
does not imply thatx!=y
is false. Accordingly, when defining__eq__()
, one should also define__ne__()
so that the operators will behave as expected.Arguments to rich comparison methods are never coerced.
A class that overrides
__eq__()
and does not define__hash__()
will have its__hash__()
implicitly set toNone
.
Arguments are never coerced (coercion in python glossary) means that checking input argument (o
in my example) is your responsibility, try:
>>> 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'
And it's even possible to compare objects of different classes:
>>> d == a
True
And about __hash__
being set to None
means, that hash(obj)
fails:
>>> hash(d)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'B'
And every collection requiring hashing also fails:
>>> set((d,))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'B'
While it works with A
:
>>> set((a,))
{<__main__.A object at 0x7f8a85fe4668>}
Upvotes: 6
Reputation: 37023
Here is a working version of your code. I included the required import of the copy
module and added colons to make it valid code.
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))
This appears to work but doesn't really (see below). Note that I added a further test to ensure that the comparison works when both True and False ... but the testing so far simply isn't good enough.
However, you should look at @Viktor's answer, since that explains that object equality checks (which you are inheriting in your class) do not check the equality of any attribute values but simply whether the two are the same object.
Upvotes: 0