Chris J Harris
Chris J Harris

Reputation: 1841

Comparing a class to None

I have a supposedly simple situation where I am trying to sort a list that contains a mix of class instances, and None. I've implemented the lt method of the class, but I'm still getting an error:

TypeError: '<' not supported between instances of 'NoneType' and 'test_class

Here's how I'm currently doing it:

class test_class:
    def __init__(self, num):
        self.num = num

    def __lt__(self, other):
        if other is None:
            return False
        else:
            return self.num < other.num

    def __eq__(self, other):
        if other is None:
            return False
        else:
            return self.num == other.num

tc1 = test_class(1)
tc2 = test_class(2)

sorted([tc1, tc2, None])

... which produces the above-mentioned error. Could anyone kindly point out what I'm doing wrong? In some sort of idealized reality where programming languages work in a common-sense way, I would have thought that the 'if other is None' bit should handle a comparison with None.

Thanks in advance!

Upvotes: 0

Views: 62

Answers (1)

Adam Smith
Adam Smith

Reputation: 54193

Note that your error is not

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'test_class' and 'NoneType'

but instead

...between instances of 'NoneType' and 'test_class'

The ordering matters. None does not implement a __lt__ method that knows how to compare to a test_class. However Python is smart enough to use the other class's __gt__ in this case.

class TestClass(object):
    def __init__(self, num):
        self.num = num

    def __lt__(self, other):
        if other is None:
            return False
        else:
            return self.num < other.num

    def __gt__(self, other):
        if other is None:
            return True
        return self.num > other.num

    def __eq__(self, other):
        if other is None:
            return False
        else:
            return self.num == other.num

What's more, functools.total_ordering can be used to decorate your class so you only have to define __eq__ and one of __lt__, __le__, __gt__, __ge__ and the rest will be auto generated for you.

import functools

@functools.total_ordering
class TestClass(object):
    def __init__(self, num):
        self.num = num

    def __lt__(self, other):
        if other is None: return False
        return self.num < other.num

    def __eq__(self, other):
        return isinstance(other, type(self)) and self.num == other.num
        # I refactored this to be a little nicer

Now TestClass() acts as if __gt__, __ge__, and __le__ are all defined, even though you only have to define one.

Upvotes: 1

Related Questions