Python Custom Sorting
August 05, 2024
Overview
On the surface, Custom Sorting in Python can be achieved primarily in two ways. However, if we look closer, we'll find out that both of these ways use the same thing. Furthermore, there's even an alternative, less common, way to achieve custom sorting.
The two mechanisms are:
- The sorting
key
function (belonging tosorted()
andlist.sort()
). - Less Than (
__lt__
) function.
Table of Contents
Sorting Key Function
The sorting key
function provides a versatile way to control the sorting order in Python iterables. By using the optional key
parameter, you can specify a function that returns a value for comparison. This value must be an object that implements a __lt__(self, other)
function, which defines the 'less than' behavior for comparisons (e.g. sorting).
This method is particularly useful when working with complex objects or when the sorting criteria involves derived or computed values. For instance, if you have a list of custom objects and you want to sort them based on a specific attribute, the key function approach is straightforward and elegant. It abstracts the comparison logic, making your code cleaner and easier to maintain.
Consider a scenario where you have a list of objects representing different entities, each with multiple attributes. By defining a key function, you can easily sort these objects by any attribute without modifying the objects themselves. This flexibility is one of the main advantages of using the key function for sorting.
class CustomObject:
def __init__(self, value):
self.value = value # value must be a type that has `__lt__` implemented.
# List of objects to sort
objects = [CustomObject(3), CustomObject(1), CustomObject(2)]
# Sorting using a key function
sorted_objects = sorted(objects, key=lambda obj: obj.value)
Less Than (__lt__) Function
The __lt__
function, also known as the 'less than' method, is a fundamental part of Python's object model. It is used by many built-in functions that rely on object comparison to complete their tasks. The list of built-in Python functions that rely on __lt__
includes:
min
functionmax
functionsorted
functionlist.sort
functionheapq
modulebisect
module
Defining a custom __lt__
method within your class allows you to control the natural ordering of your objects. This method should return True
if the object is considered less than the other object, and False otherwise. This approach is ideal when you want to encapsulate the comparison logic within the class, promoting encapsulation and making the class self-contained.
For example, if you have a class representing a complex data structure, defining the __lt__
method ensures that all instances of the class can be compared directly. This is particularly useful for operations like sorting, finding the minimum or maximum values, and more.
class CustomObject:
def __init__(self, value):
self.value = value
def __lt__(self, other):
return self.value < other.value
# List of objects to sort
objects = [CustomObject(3), CustomObject(1), CustomObject(2)]
# Sorting using the custom __lt__ method
sorted_objects = sorted(objects)
When implementing a __lt__
method, it's common to implement the other comparison methods:
__eq__
:==
__ne__
:!=
__gt__
:>
__le__
:<=
__ge__
:>=
Other
For completeness, it's important to mention Python's built-in functools.cmp_to_key
function. This function converts the classic comparison function behavior, common in other languages like Java, to a function that can be used as the key parameter of sorted
and list.sort
functions. This compatibility layer allows you to reuse traditional comparison logic in Python's modern sorting mechanisms.
The cmp_to_key
function is particularly useful when migrating code from other programming languages or when you need to implement complex comparison logic that doesn't fit neatly into a key function. By wrapping your comparison function with cmp_to_key
, you can leverage existing code and maintain consistency across different parts of your application.
from functools import cmp_to_key
def compare(a, b):
if a.value < b.value:
return -1
elif a.value > b.value:
return 1
else:
return 0
class CustomObject:
def __init__(self, value):
self.value = value
# List of objects to sort
objects = [CustomObject(3), CustomObject(1), CustomObject(2)]
# Sorting using cmp_to_key
sorted_objects = sorted(objects, key=cmp_to_key(compare))
Conclusion
The sorted
and list.sort
functions' key
functions are most often utilized to return some value that is of a type that
does have the __lt__
function defined, but this may not always be possible, or it may be cleaner given the use case to implement a __lt__
function alongside its counterparts (learn more about Python magic methods).
At the end of the day, a __lt__
function is called (but not necessarily one that is implemented by you).