Quick answer: __lt__ implements the less-than operation used by < and sorting. Compare a stable domain key, return NotImplemented for unsupported operand types, and keep the less-than, equality, and hashing contracts consistent for custom objects.

Python __lt__() is the special method that defines how custom objects behave with the less-than operator, <. If you write a < b, Python may call a.__lt__(b) behind the scenes. This is part of Python's data model for rich comparisons.
Use __lt__() when your class has a natural ordering. For example, products might be sorted by price, students by score, or versions by major and minor numbers. The official Python data model documents object.__lt__, and the operator.lt() helper exposes the same less-than operation as a function.
Basic Python __lt__() Example
Define __lt__(self, other) inside a class and return True when self should be considered less than other. Here is a simple price comparison.
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __lt__(self, other):
return self.price < other.price
book = Product("Book", 12)
pen = Product("Pen", 2)
print(pen < book)
The expression pen < book returns True because the pen's price is lower. This does not compare every attribute. It compares exactly the rule you write in __lt__(). Keep the rule simple and deterministic; comparison methods should not change object state.
How Python Uses __lt__()
Python calls rich comparison methods when an operator needs them. For less-than comparisons, the left operand gets the first chance to answer. If that object cannot compare itself to the right operand, it should return NotImplemented. Python may then try another comparison path or raise TypeError. This protocol is why special methods are usually invoked by operators instead of being called directly.
Use __lt__() With sorted()
Python's sorting tools can use __lt__() when no key function is supplied. That makes the method useful for classes that have one obvious ordering.
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __lt__(self, other):
return self.score < other.score
def __repr__(self):
return f"Student({self.name!r}, {self.score})"
students = [Student("Ada", 91), Student("Lin", 84), Student("Max", 98)]
print(sorted(students))
This sorts students by score from lowest to highest. For tuple-based sorting patterns, see sort a list of tuples in Python. Python's own sorting HOWTO is also a good reference for key functions and ordering behavior.

Return NotImplemented for Unknown Types
A robust __lt__() method should return NotImplemented when it does not know how to compare the other object. This lets Python try the reflected operation or raise a clear TypeError.
class Version:
def __init__(self, major, minor):
self.major = major
self.minor = minor
def __lt__(self, other):
if not isinstance(other, Version):
return NotImplemented
return (self.major, self.minor) < (other.major, other.minor)
print(Version(3, 10) < Version(3, 12))
Returning False for every unknown type can hide bugs. NotImplemented is more accurate because it says, “this class does not define that comparison.” This is especially important in shared code where objects from different libraries may accidentally be compared.
Add Equality and total_ordering
If your objects support ordering, they usually need equality too. The functools.total_ordering decorator can fill in the other comparison methods when you define __eq__() and one ordering method such as __lt__().
from functools import total_ordering
@total_ordering
class Version:
def __init__(self, major, minor):
self.major = major
self.minor = minor
def __eq__(self, other):
if not isinstance(other, Version):
return NotImplemented
return (self.major, self.minor) == (other.major, other.minor)
def __lt__(self, other):
if not isinstance(other, Version):
return NotImplemented
return (self.major, self.minor) < (other.major, other.minor)
print(Version(3, 9) <= Version(3, 10))
This reduces repeated comparison code. Use it when readability matters more than a tiny amount of comparison overhead. For class-method vocabulary, the guide to Python cls vs self is a useful companion.

Use operator.lt()
The operator module provides function versions of operators. operator.lt(a, b) is equivalent to a < b, so it uses the same comparison rules.
import operator
print(operator.lt(3, 5))
print(operator.lt("apple", "banana"))
This is useful when you need to pass the less-than operation as a function. In ordinary class code, the < operator is usually clearer.
When to Use key Instead of __lt__()
Do not add __lt__() just because you need one sorted view of a class. A key function is better when different screens, reports, or workflows need different sorting rules.
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
employees = [Employee("Ada", 120), Employee("Lin", 105)]
by_salary = sorted(employees, key=lambda employee: employee.salary)
print([employee.name for employee in by_salary])
Use __lt__() for the class's natural ordering. Use key=... for one-off sorting. This keeps the class behavior predictable and avoids surprising comparisons. A good test is whether two developers would independently choose the same ordering for the class. If not, prefer a key function.
Testing __lt__() Methods
Test comparison methods with more than one pair of objects. Check a less-than case, a greater-than case, equality if you support it, and an unsupported type. Also test sorting a short list. These checks catch reversed operators, missing type guards, and inconsistent ordering rules before the class is used in larger workflows.

Common Mistakes
The less-than operator is <, not >. __lt__() should answer whether the left object is less than the right object. Another mistake is comparing unrelated types without a type check. Use NotImplemented when the other object is not supported.
Also remember that __lt__() does not replace object initialization. Use __init__() to store fields and __lt__() to compare already-created objects. For the initialization method, see Python __init__. For related class organization, see nested classes in Python and Python super().
Conclusion
Use Python __lt__() to define less-than behavior for custom objects when the class has one natural order. Return a boolean for supported comparisons, return NotImplemented for unsupported types, and pair ordering with equality when needed. For one-off sorting needs, prefer a key function instead of changing the class's comparison behavior.
Compare A Stable Key
A tuple of fields gives a predictable lexicographic order and keeps comparison logic in one place. Only include fields that define the domain order.
class User:
def __init__(self, name, score):
self.name = name
self.score = score
def __lt__(self, other):
if not isinstance(other, User):
return NotImplemented
return (self.score, self.name) < (other.score, other.name)
print(User("Ada", 90) < User("Grace", 95))

Return NotImplemented
Returning NotImplemented lets Python handle reflected comparisons or raise the appropriate TypeError. Returning False for every unrelated type can hide a bug.
class Token:
def __init__(self, value):
self.value = value
def __lt__(self, other):
if not isinstance(other, Token):
return NotImplemented
return self.value < other.value
print(Token(1) < Token(2))
Keep Equality Consistent
If two objects compare equal by the same key, their ordering should not contradict that equality. Define __eq__ and __hash__ deliberately when objects are used in sets or dictionaries.
from functools import total_ordering
@total_ordering
class Version:
def __init__(self, major):
self.major = major
def __eq__(self, other):
if not isinstance(other, Version):
return NotImplemented
return self.major == other.major
def __lt__(self, other):
if not isinstance(other, Version):
return NotImplemented
return self.major < other.major
print(Version(1) <= Version(2))
Test Sorting And Mixed Types
Test equal keys, reversed order, empty collections, and unsupported operands. Sorting is an integration check that exercises the comparison contract repeatedly.
values = [3, 1, 2]
print(sorted(values))
Python’s data model documents rich comparisons and NotImplemented. Related references include sorting keys, record ordering, and comparison tests.
For related ordering logic, compare sorting keys, record ordering, and comparison tests when defining rich comparisons.
Frequently Asked Questions
What does __lt__ do in Python?
It implements the less-than operation used by < and by ordering algorithms such as sorted.
Why return NotImplemented?
It lets Python try the reflected or other operand’s comparison and produces the correct unsupported-type behavior.
What should __lt__ compare?
Compare a stable tuple or key containing the fields that define the domain’s ordering.
Should I write every comparison method?
Use functools.total_ordering cautiously or define the full ordering contract explicitly when performance and semantics matter.
great! thank you