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' >>> ```

Original source

Related problems