Sorting list by an attribute that can be None Sorting list by an attribute that can be None python-3.x python-3.x

Sorting list by an attribute that can be None


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 < NoneFalse>>> 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 amy_list.sort(key=nonesorter)


For a general solution, you can define an object that compares less than any other object:

from functools import total_ordering@total_orderingclass MinType(object):    def __le__(self, other):        return True    def __eq__(self, other):        return (self is other)Min = MinType()

Then use a sort key that substitutes Min for any None values in the list

mylist.sort(key=lambda x: Min if x is None else x)


The solutions proposed here work, but this could be shortened further:

mylist.sort(key=lambda x: x or 0)

In essence, we can treat None as if it had value 0.

E.g.:

>>> mylist = [3, 1, None, None, 2, 0]>>> mylist.sort(key=lambda x: x or 0)>>> mylist[None, None, 0, 1, 2, 3]