bisect
The bisect module provides functions for binary search and sorted insertion in lists. These tools let you find insertion points and maintain sorted order without manually searching through your data — essential for any scenario where you need to keep a list sorted while adding elements.
Installation
The bisect module is built into Python’s standard library. No installation is required:
import bisect
bisect_left()
The bisect_left() function finds the leftmost insertion point for a value in a sorted list. If the value already exists in the list, it returns the position of the first occurrence.
import bisect
numbers = [1, 3, 5, 7, 9]
# Find where 4 would fit (between 3 and 5)
pos = bisect.bisect_left(numbers, 4)
print(f"Insert 4 at position: {pos}")
# Insert 4 at position: 2
# With duplicates - returns first position
data = [1, 2, 2, 2, 3]
left_pos = bisect.bisect_left(data, 2)
print(f"bisect_left: {left_pos}")
# bisect_left: 1
Syntax
bisect.bisect_left(a, x, lo=0, hi=len(a))
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
a | sequence | — | Sorted list to search |
x | any | — | Value to find insertion point for |
lo | int | 0 | Lower bound index |
hi | int | len(a) | Upper bound index |
bisect_right()
The bisect_right() function finds the rightmost insertion point. If the value already exists, it returns the position after all existing occurrences.
import bisect
numbers = [1, 3, 5, 7, 9]
# Find where 5 would fit (at the rightmost position)
pos = bisect.bisect_right(numbers, 5)
print(f"Insert 5 at position: {pos}")
# Insert 5 at position: 3
# With duplicates - returns position after all 2s
data = [1, 2, 2, 2, 3]
right_pos = bisect.bisect_right(data, 2)
print(f"bisect_right: {right_pos}")
# bisect_right: 4
Related Functions
The module also provides insort_left() and insort_right(), which combine finding the insertion point and inserting the value in one step:
import bisect
sorted_numbers = [1, 3, 5, 7, 9]
# Insert 4 using insort_right (insort_left works similarly)
bisect.insort_right(sorted_numbers, 4)
print(sorted_numbers)
# [1, 3, 4, 5, 7, 9]
# Insert 5 again - goes after existing 5
bisect.insort_right(sorted_numbers, 5)
print(sorted_numbers)
# [1, 3, 4, 5, 5, 7, 9]
Common Patterns
Maintaining a sorted collection
import bisect
class SortedList:
def __init__(self):
self._data = []
def add(self, item):
pos = bisect.bisect_left(self._data, item)
self._data.insert(pos, item)
def __contains__(self, item):
pos = bisect.bisect_left(self._data, item)
return pos < len(self._data) and self._data[pos] == item
sl = SortedList()
sl.add(5)
sl.add(2)
sl.add(8)
print(sl._data) # [2, 5, 8]
Lookup in sorted data (threshold mapping)
import bisect
thresholds = [0, 18, 35, 50, 65, 100]
labels = ["child", "young adult", "adult", "middle-aged", "senior", "elderly"]
def get_category(age):
pos = bisect.bisect_right(thresholds, age) - 1
return labels[max(0, pos)]
print(get_category(10)) # child
print(get_category(25)) # young adult
print(get_category(55)) # middle-aged
Performance
Binary search via bisect functions runs in O(log n) time, compared to O(n) for linear search. For a list of 1 million elements, binary search takes about 20 comparisons — dramatically faster for large datasets.
See Also
- collections — Specialized container datatypes for queues, counters, and default values
- itertools — Iterator helpers that pair well with sorted-data workflows
- list — The built-in sequence type most often used with bisect operations