how to compare for equality for None objects in custom class in python?
class, comparison, equals, object, python
Solution
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
Problem
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' >>> ```