Search code examples
pythonpython-3.xpython-2to3

Sorting list by an attribute that can be None


I'm trying to sort a list of objects using

my_list.sort(key=operator.attrgetter(attr_name))

but if any of the list items has attr = None instead of attr = 'whatever',

then I get a TypeError: unorderable types: NoneType() < str()

In Py2 it wasn't a problem. How do I handle this in Py3?


Solution

  • The ordering comparison operators are stricter about types in Python 3, as described here:

    The ordering comparison operators (<, <=, >=, >) raise a TypeError exception when the operands don’t have a meaningful natural ordering.

    Python 2 sorts None before any string (even empty string):

    >>> None < None
    False
    
    >>> None < "abc"
    True
    
    >>> None < ""
    True
    

    In Python 3 any attempts at ordering NoneType instances result in an exception:

    >>> None < "abc"
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: unorderable types: NoneType() < str()
    

    The quickest fix I can think of is to explicitly map None instances into something orderable like "":

    my_list_sortable = [(x or "") for x in my_list]
    

    If you want to sort your data while keeping it intact, just give sort a customized key method:

    def nonesorter(a):
        if not a:
            return ""
        return a
    
    my_list.sort(key=nonesorter)