operator Module
The operator module turns Python's built-in operators (+, ==, indexing, attribute access) into ordinary callable functions, which is exactly what higher-order functions like sorted, reduce, and map need instead of a lambda.
Expressions like a + b or a[i] are operators, not functions — you cannot pass + itself as an argument to sorted() or reduce(). The operator module (standard library) provides function equivalents for nearly every operator, plus convenient "key function" builders (itemgetter, attrgetter, methodcaller) that are faster and more readable than an equivalent lambda in many common cases.
What the operator Module Provides
For nearly every Python operator, operator exposes a plain function that does the same thing: operator.add(a, b) is equivalent to a + b, operator.eq(a, b) to a == b, operator.getitem(a, i) to a[i], and so on. This matters because functions like map(), reduce(), and sorted(key=...) need an actual callable — using operator.add instead of lambda a, b: a + b is shorter, and in CPython, typically faster since it skips an extra Python-level function call layer.
import operator
print(operator.add(3, 4)) # 7 — same as 3 + 4
print(operator.mul(3, 4)) # 12 — same as 3 * 4
print(operator.eq(3, 4)) # False — same as 3 == 4
print(operator.getitem([1, 2, 3], 1)) # 2 — same as [1, 2, 3][1]Arithmetic and Comparison Operator Functions
operator covers arithmetic (add, sub, mul, truediv, floordiv, mod, pow, neg), comparisons (eq, ne, lt, le, gt, ge), and logical/bitwise operators (and_, or_, not_, xor) — note the trailing underscore on names that would otherwise collide with Python keywords (and, or, not).
import operator
print(operator.sub(10, 3)) # 7
print(operator.truediv(7, 2)) # 3.5
print(operator.floordiv(7, 2)) # 3
print(operator.lt(3, 5)) # True
print(operator.and_(True, False)) # FalseUsing operator with reduce
This is one of operator's most common uses: replacing a small lambda inside reduce() with a direct, named operator function.
from functools import reduce
import operator
numbers = [1, 2, 3, 4, 5]
total = reduce(operator.add, numbers)
print(total) # 15
product = reduce(operator.mul, numbers)
print(product) # 120itemgetter — Key Functions for Indexing and Item Access
operator.itemgetter(key) returns a callable that fetches obj[key] — ideal as the key= argument to sorted(), min(), max() when sorting by index or dict key. It can also take multiple keys at once, returning a tuple of values.
from operator import itemgetter
people = [("Alice", 30), ("Bob", 25), ("Cara", 35)]
by_age = sorted(people, key=itemgetter(1))
print(by_age) # [('Bob', 25), ('Alice', 30), ('Cara', 35)]
records = [{"name": "Alice", "score": 90}, {"name": "Bob", "score": 85}]
by_score = sorted(records, key=itemgetter("score"), reverse=True)
print(by_score) # [{'name': 'Alice', 'score': 90}, {'name': 'Bob', 'score': 85}]
# multiple keys -> sorts by a tuple, like (last, first)
data = [("Smith", "Anna"), ("Doe", "Jane"), ("Smith", "Amy")]
print(sorted(data, key=itemgetter(0, 1)))
# [('Doe', 'Jane'), ('Smith', 'Amy'), ('Smith', 'Anna')]attrgetter — Key Functions for Attribute Access
operator.attrgetter(name) returns a callable that fetches obj.name, useful for sorting or extracting from objects (instances of classes) rather than dicts or tuples. It also supports dotted paths for nested attribute access.
from operator import attrgetter
class Person:
def __init__(self, name: str, age: int) -> None:
self.name, self.age = name, age
def __repr__(self) -> str:
return f"Person({self.name!r}, {self.age})"
people = [Person("Alice", 30), Person("Bob", 25), Person("Cara", 35)]
by_age = sorted(people, key=attrgetter("age"))
print(by_age) # [Person('Bob', 25), Person('Alice', 30), Person('Cara', 35)]methodcaller — Calling a Named Method as a Key or Callback
operator.methodcaller(name, *args) returns a callable that invokes obj.name(*args) — useful when you need to call the same method across many objects, such as in map() or as a sort key.
from operator import methodcaller
words = ["banana", "Apple", "cherry"]
lowered = list(map(methodcaller("lower"), words))
print(lowered) # ['banana', 'apple', 'cherry']
by_lower = sorted(words, key=methodcaller("lower"))
print(by_lower) # ['Apple', 'banana', 'cherry']Comparison: operator Functions vs Equivalent lambdas
Every operator function has a lambda equivalent — the difference is mainly readability and (in CPython) a small performance edge from avoiding an extra Python-level call frame.
operator.add same as lambda a, b: a + b
operator.itemgetter(1) same as lambda x: x[1]
operator.attrgetter("age") same as lambda x: x.age
operator.methodcaller("f") same as lambda x: x.f()Functional Composition Use Cases
Because operator functions are ordinary callables, they compose naturally with other higher-order functions: passing operator.add to reduce(), operator.itemgetter(...) to sorted()/min()/max(), or operator.attrgetter(...) to sorted() when grouping with itertools.groupby() (which requires input pre-sorted by the same key).
from itertools import groupby
from operator import attrgetter
class Employee:
def __init__(self, name: str, dept: str) -> None:
self.name, self.dept = name, dept
employees = [
Employee("Ana", "Engineering"),
Employee("Ben", "Sales"),
Employee("Cid", "Engineering"),
]
employees.sort(key=attrgetter("dept")) # groupby requires pre-sorted input
for dept, group in groupby(employees, key=attrgetter("dept")):
print(dept, [e.name for e in group])
# Engineering ['Ana', 'Cid']
# Sales ['Ben']Practical Usage
Reach for operator.itemgetter/attrgetter/methodcaller any time you'd otherwise write a trivial lambda purely to extract or invoke something for sorted(), min(), max(), map(), or reduce() — they are the idiomatic, slightly faster, and arguably clearer alternative in exactly those spots.
Key Takeaways
operator provides function equivalents for Python's built-in operators (add, eq, getitem, etc.), so operators can be passed as ordinary callables to map(), reduce(), and similar higher-order functions.
itemgetter(key) and attrgetter(name) build key functions for sorted()/min()/max() that extract by index/dict-key or by attribute name, respectively — and both accept multiple keys/names for compound sorting.
methodcaller(name, *args) builds a callable that invokes a named method on whatever object it's given.
Names that would collide with Python keywords get a trailing underscore: and_, or_, not_.
Using operator functions instead of equivalent one-line lambdas is generally more readable and, in CPython, typically slightly faster.
from functools import reduce total = reduce(lambda a, b: a + b, [1, 2, 3, 4]) # works, but wordier than needed from functools import reduce import operator total = reduce(operator.add, [1, 2, 3, 4]) # equivalent, more direct operator.add is a drop-in, named replacement for the common lambda a, b: a + b pattern — it reads more directly and avoids an extra lambda-call layer.
people = [("Bob", 25), ("Alice", 30)] by_age = sorted(people, key=lambda p: p[1]) # works, but a plain lambda for indexing from operator import itemgetter people = [("Bob", 25), ("Alice", 30)] by_age = sorted(people, key=itemgetter(1)) # idiomatic key function itemgetter(1) directly expresses 'sort by index 1' and is the idiomatic choice here over an equivalent lambda p: p[1].
import operator result = operator.and(True, False) # SyntaxError: 'and' is a Python keyword import operator result = operator.and_(True, False) # trailing underscore avoids the keyword clash
Operator names that collide with Python keywords (and, or, not) are spelled with a trailing underscore in the operator module: and_, or_, not_.
Why does the operator module exist when Python already has operators like + and ==?
Operators like + and == are syntax, not callable objects — they cannot be passed directly as arguments to functions like sorted(), map(), or reduce(). The operator module wraps each operator as an ordinary function so it CAN be passed around and composed like any other callable.
What does operator.itemgetter(1) return when called, and where is it commonly used?
It returns a callable that, given an object obj, returns obj[1]. It's commonly used as the key= argument to sorted(), min(), or max() when sorting sequences of tuples, lists, or dicts by a specific index or key.
How does operator.attrgetter differ from operator.itemgetter?
attrgetter(name) fetches an ATTRIBUTE (obj.name), useful for sorting/extracting from class instances; itemgetter(key) fetches an ITEM via indexing/subscripting (obj[key]), useful for tuples, lists, and dicts.
What does operator.methodcaller('upper') do when used as a map() function?
It returns a callable that calls obj.upper() on whatever object it's given, so map(methodcaller('upper'), words) calls .upper() on every word in the iterable.
Why are some operator module function names spelled with a trailing underscore, like and_ and or_?
Because and, or, and not are Python keywords and cannot be used as function names; the operator module adds a trailing underscore (and_, or_, not_) to avoid the naming collision.
How can itemgetter or attrgetter accept multiple keys, and what does the result look like?
Passing multiple arguments, e.g. itemgetter(0, 1) or attrgetter('last', 'first'), returns a callable that produces a TUPLE of the corresponding values, enabling compound/multi-key sorting in one key function.
Is operator.add(a, b) meaningfully different in behavior from a + b?
No — operator.add(a, b) performs exactly the same operation as a + b; the only difference is that operator.add is an ordinary callable object that can be passed as an argument, whereas + is operator syntax that cannot.
Use functools.reduce together with operator.mul to compute the factorial of 6 (i.e., the product of 1 through 6).
from functools import reduce import operator n = 6 factorial = reduce(operator.mul, range(1, n + 1)) print(factorial) # 720
720 — reduce(operator.mul, range(1, 7)) multiplies 1*2*3*4*5*6, giving 720.
What does this print?
Gizmo — itemgetter('price') extracts each dict's 'price' value as the comparison key, and min() finds the item with the smallest price (4.99), which is the Gizmo.
Write a function sort_by_multiple(records) that sorts a list of (last_name, first_name, age) tuples first by last_name, then by first_name, using operator.itemgetter with multiple keys.
from operator import itemgetter def sort_by_multiple(records: list[tuple[str, str, int]]) -> list[tuple[str, str, int]]: return sorted(records, key=itemgetter(0, 1)) people = [ ("Smith", "Bob", 40), ("Doe", "Jane", 28), ("Smith", "Amy", 35), ] print(sort_by_multiple(people)) # [('Doe', 'Jane', 28), ('Smith', 'Amy', 35), ('Smith', 'Bob', 40)] itemgetter(0, 1) builds a (last_name, first_name) tuple key for each record, so sorted() orders first by last_name and breaks ties by first_name — producing [('Doe','Jane',28), ('Smith','Amy',35), ('Smith','Bob',40)].
from operator import itemgetter
inventory = [
{"name": "Widget", "price": 9.99, "qty": 3},
{"name": "Gadget", "price": 19.99, "qty": 1},
{"name": "Gizmo", "price": 4.99, "qty": 10},
]
cheapest = min(inventory, key=itemgetter("price"))
print(cheapest["name"])