'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 1:[1]

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

from functools import total_ordering

@total_ordering
class 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)

Solution 2:[2]

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]

Solution 3:[3]

Since there are other things besides None that are not comparable to a string (ints and lists, for starters), here is a more robust solution to the general problem:

my_list.sort(key=lambda x: x if isinstance(x, str) else "")

This will let strings and any type derived from str to compare as themselves, and bin everything else with the empty string. Or substitute a different default default key if you prefer, e.g. "ZZZZ" or chr(sys.maxunicode) to make such elements sort at the end.

Solution 4:[4]

A general solution to deal with None independent of the types to be sorted:

my_list.sort(key=lambda x: (x is not None, x))

putting None values first.

Note that: my_list.sort(key=lambda x: (x is None, x)) puts None values last.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 augurar
Solution 2
Solution 3 alexis
Solution 4