Reputation: 31252
I am writing a Queue
data structure for python purely for learning purposes. here is my class
. when I compare two Queue
object for equality, I get error. I think the error pops up, because I dont compare for None
in my __eq__
.but how can I check for None
and return
accordinly. in fact, I am using list
under the hood and calling its __eq__
, thinking it should take care as shown here, but it does not
>>> l=[1,2,3]
>>> l2=None
>>> l==l2
False
Here is my class:
@functools.total_ordering
class Queue(Abstractstruc,Iterator):
def __init__(self,value=[],**kwargs):
objecttype = kwargs.get("objecttype",object)
self.container=[]
self.__klass=objecttype().__class__.__name__
self.concat(value)
def add(self, data):
if (data.__class__.__name__==self.__klass or self.__klass=="object"):
self.container.append(data)
else:
raise Exception("wrong type being added")
def __add__(self,other):
return Queue(self.container + other.container)
def __iadd__(self,other):
for i in other.container:
self.add(i)
return self
def remove(self):
return self.container.pop(0)
def peek(self):
return self.container[0]
def __getitem__(self,index):
return self.container[index]
def __iter__(self):
return Iterator(self.container)
def concat(self,value):
for i in value:
self.add(i)
def __bool__(self):
return len(self.container)>0
def __len__(self):
return len(self.container)
def __deepcopy__(self,memo):
return Queue(copy.deepcopy(self.container,memo))
def __lt__(self,other):
return self.container.__lt__(other.container)
def __eq__(self, other):
return self.container.__eq__(other.container)
But when I try compare using the above class I get:
>>> from queue import Queue
>>> q = Queue([1,2,3])
>>> q
>>> print q
<Queue: [1, 2, 3]>
>>> q1 = None
>>> q==q1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "queue.py", line 65, in __eq__
return self.container.__eq__(other.container)
AttributeError: 'NoneType' object has no attribute 'container'
>>>
Upvotes: 3
Views: 4245
Reputation: 35069
Tell Python that you don't know how to compare against other types:
def __eq__(self, other):
if not isinstance(other, Queue):
return NotImplemented
return self.container.__eq__(other.container)
you might consider checking hasattr(other, 'container')
instead of the isinstance
, or catch the AttributeError
.
But the important thing is that, unlike other answers recommend, you do not want to return False
when other
isn't a Queue. If you return NotImplemented
, Python will give other
a chance to check the equality; if you return False, it won't. Differentiate between the three possible answers to the question "are these objects equal": yes, no, and I don't know.
You'll want to do something similar in your __lt__
, where the difference is even more apparent: if you return False
from both __lt__
and __eq__
, then the __gt__
inserted by total_ordering
will return True - even though you can't do the comparison. If you return NotImplemented
from both of them, it will also be NotImplemented
.
Upvotes: 3
Reputation:
Your problem is how you are implementing __eq__
.
Look at this code:
q = Queue([1,2,3])
q1 = None
q==q1
And lets rewrite it as the equivilent:
q = Queue([1,2,3])
q == None
Now, in Queue.__eq__
we have:
def __eq__(self, other):
return self.container.__eq__(other.container)
But other
is None
, which means the return statement is calling:
self.container.__eq__(None.container)
As your error rightly states:
'NoneType' object has no attribute 'container'
Because it doesn't! None
doesn't have a container attribute.
So, the way to do it, depends on how you want to treat it. Now, obviously, a Queue
object can't be None
if its been defined, so:
return other is not None and self.container.__eq__(other.container)
Will lazily evaluate if other
is None
, and return False
before evalauting the part of the expression after the and
. Otherwise, it will perform the evaulation. However, you will get other issues if other
is not of type Queue
(or more correctly the other object doesn't have a container
attribute), such as:
q = Queue([1,2,3])
q == 1
>>> AttributeError: 'int' object has no attribute 'container'
So... depending on your logic, and if a Queue
can't be "equal" to other types (which is something only you can say), you can check for the correct type like so:
return other is not None and type(self) == type(other) and self.container.__eq__(other.container)
But... None
is a NoneType
, so it can never be of the same type as a Queue
. So we can shorten it again to just:
return type(self) == type(other) and self.container.__eq__(other.container)
edit: As per mglisons comments:
This could be made more pythonic by using the regular equality statement:
return type(self) == type(other) and self.container == other.container
They have also raised a good point regarding the use of type
in checking eaulity. If you are certain that Queue
would never be subclassed (which is difficult to state). You could use exception handling to capture the AttributeError
effectively, like so:
def __eq__(self, other):
try:
return self.container == other.container
except AttributeError:
return False # There is no 'container' attribute, so can't be equal
except:
raise # Another error occured, better pay it forward
The above may be considered a little overengineered, but is probably one of the better ways to approach this from a safety and resuability perspective.
Or a better, shorter approach (which I should have thought of initially) using hasattr
is:
return hasattr(other, 'container') and self.container == other.container
Upvotes: 4
Reputation: 6489
you can do something like
def __eq__(self,other):
if other is None: return False
return self.container.__eq__(other.container)
You may also want to do something like
if not isinstance(other,Queue): return False
Upvotes: 1