Back to Blog
Python

python **le** (<=) in Python: Syntax and Custom Types

python **le**: Understand Python's <= operator, its __le__ implementation, chaining behavior, and how to use it with custom classes and sorting.

Pythoncomparison operatorsdunder methodsoperator overloadingcustom classes
A visual representation of the Python less-than-or-equal operator with custom class comparison.

The <= operator in Python compares two values and returns True when the left operand is less than or equal to the right operand. This operator, often referred to as le in documentation and in the operator module, is implemented by the __le__ method on each operand. Understanding how python **le** behaves across built-in types and how to implement it for custom classes is essential for writing correct and maintainable comparisons.

Basic Usage with Numbers and Strings

For numeric types, <= performs the expected arithmetic comparison. For strings, it compares lexicographically using Unicode code points. Both behaviors are built into the language and require no extra code.

print(3 <= 5) # True print(5 <= 5) # True print("apple" <= "banana") # True print("apple" <= "apple") # True

The result is always a bool. When the operands are of incompatible types, such as an int and a str, Python raises a TypeError because the comparison is not defined for those types.

Chaining Comparisons

Python supports chaining comparison operators. The expression a <= b <= c is evaluated as a <= b and b <= c, but b is evaluated only once. This is useful for range checks.

x = 5 if 0 <= x <= 10: print("x is between 0 and 10")

Chaining works with any comparison operator, and the short-circuit behavior applies: if the first comparison fails, the second is not evaluated.

How <= Works for Custom Classes

When you define a class, you can control how <= behaves by implementing the __le__ method. This method takes two arguments: self and other, and must return a boolean or NotImplemented.

class Point: def __init__(self, x, y): self.x = x self.y = y def __le__(self, other): if isinstance(other, Point): return (self.x, self.y) <= (other.x, other.y) return NotImplemented p1 = Point(1, 2) p2 = Point(3, 4) print(p1 <= p2) # True

The NotImplemented return tells Python to try the reflected operation on the other operand. If both return NotImplemented, Python raises TypeError.

Using <= with Collections and Sorting

The <= operator is also used internally by sorting functions and sorted() to determine ordering. For lists of numbers or strings, the default behavior works. For custom objects, you need to implement the comparison methods. Instead of writing all six comparison dunders, you can use functools.total_ordering to fill in the rest from __le__ and __eq__.

from functools import total_ordering @total_ordering class Person: def __init__(self, name, age): self.name = name self.age = age def __le__(self, other): return self.age <= other.age def __eq__(self, other): return self.age == other.age people = [Person("Alice", 30), Person("Bob", 25)] people.sort() print([p.name for p in people]) # ['Bob', 'Alice']

Common Mistakes and Edge Cases

A frequent mistake is assuming <= performs a deep comparison on lists or dictionaries. For lists, <= compares element-wise and returns True if the left list is a prefix of the right list or if the first differing element is smaller. This is not the same as subset or containment.

print([1, 2] <= [1, 2, 3]) # True print([1, 3] <= [1, 2]) # False

Another edge case is comparing None with numbers. None <= 5 raises TypeError because the types are incompatible. Also, floating-point comparisons can behave unexpectedly due to precision, but that is inherent to IEEE 754.

Performance and Implementation Notes

The <= operator is a fast built-in operation for primitive types. For custom classes, the method call overhead is minimal but can matter in tight loops. If you need to sort large collections of custom objects, implementing __lt__ (less than) is often more efficient than __le__ because sorting algorithms rely primarily on <. The functools.total_ordering decorator adds convenience but introduces extra method calls; if performance is critical, implement the comparison methods manually.

The operator module provides a function operator.le(a, b) that calls a <= b. This is useful when you need to pass a comparison function to functions like sorted() or heapq.

import operator sorted_points = sorted(points, key=operator.attrgetter('x'), cmp=operator.le)

Note that cmp is deprecated in Python 3; use functools.cmp_to_key instead.

When to Implement __le__ vs Other Comparison Methods

The decision to implement __le__ depends on the ordering semantics you need. If your class represents a total order, implement all six methods or use total_ordering. If only a partial order is needed, implement only the methods that make sense. For example, a set-like class might implement <= to mean subset, which is not a total order.

class CustomSet: def __init__(self, items): self.items = set(items) def __le__(self, other): return self.items.issubset(other.items)

This gives <= a different meaning than the default, and it is important to document that behavior for other developers.

python **le** (<=) in Python: Syntax and Custom Types | RYUSLOG DEV