Home› Tutorials› Standard Library
Standard LibraryPython's operator Module: itemgetter, attrgetter, and Named Operators as Functions
- The
operatormodule gives function equivalents of every infix operator, useful anywhere a callable is required instead of an expression. itemgetter(key)extracts a value by index or dict key, and is a common, faster alternative to a lambda as a sort key.attrgetter("name")does the same for object attributes, including dotted paths like"address.city".methodcaller("upper")calls a named method on whatever it's given, useful when mapping the same method call across a collection.
Operators as functions: add, mul, and friends
Every infix operator in Python has a corresponding function in the operator module — operator.add(a, b) is exactly a + b, operator.eq(a, b) is a == b, and so on:
import operator
from functools import reduce
numbers = [1, 2, 3, 4]
total = reduce(operator.add, numbers) # same as reduce(lambda a,b: a+b, numbers)
product = reduce(operator.mul, numbers)
This matters anywhere Python asks for a callable rather than accepting an expression directly — reduce() being the clearest example, since it takes a two-argument function. operator.add is functionally identical to lambda a, b: a + b, but it's a named, pre-existing function rather than an anonymous one you write out, which is both slightly faster (no bytecode for a lambda body to interpret) and arguably more readable once you recognise the module.
itemgetter for sorting by index or key
operator.itemgetter builds a function that extracts a value by subscripting — index for a sequence, key for a mapping:
records = [("Ada", 1815), ("Grace", 1906), ("Alan", 1912)]
by_year = sorted(records, key=operator.itemgetter(1))
# [('Ada', 1815), ('Grace', 1906), ('Alan', 1912)]
people = [{"name": "Ada", "age": 36}, {"name": "Bob", "age": 24}]
by_age = sorted(people, key=operator.itemgetter("age"))
itemgetter also accepts multiple indices at once, which returns a tuple of extracted values — handy as a multi-field sort key: itemgetter("last", "first") sorts by last name, then first name, in one call, without writing a lambda that builds a tuple manually.
attrgetter for sorting by attribute
operator.attrgetter is the same idea for attribute access instead of subscripting, and supports dotted paths for nested attributes:
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
staff = [Employee("Nadia", 72000), Employee("Omar", 65000)]
by_salary = sorted(staff, key=operator.attrgetter("salary"))
names_only = list(map(operator.attrgetter("name"), staff))
The dotted-path support — attrgetter("manager.name") — is genuinely hard to replicate cleanly with a lambda once nesting gets involved, since the lambda would need to handle the case where an intermediate attribute is missing, while attrgetter just lets the resulting AttributeError propagate the same way direct attribute access would.
methodcaller for calling a method across a collection
operator.methodcaller builds a function that calls a named method, with any fixed arguments, on whatever object it's given:
words = ["Hello", "World"]
upper_words = list(map(operator.methodcaller("upper"), words))
# ['HELLO', 'WORLD']
padded = list(map(operator.methodcaller("rjust", 10, "*"), words))
This is equivalent to map(lambda s: s.upper(), words) but names the operation being performed rather than wrapping it in an anonymous function, which reads more directly when skimming a pipeline of map()/filter() calls. It composes well with the string methods covered elsewhere on this site, since most of those methods work directly with methodcaller.
Why bother instead of a lambda
Every example above has a one-line lambda equivalent, so the value here is incremental rather than transformative. Three real reasons to prefer the named functions: they're marginally faster, since CPython implements them in C rather than interpreting a lambda's bytecode on every call; they're picklable, which matters for multiprocessing, where a plain lambda cannot be sent to a worker process at all; and code that reads key=operator.attrgetter("created_at") tells a reviewer exactly what's being extracted without them mentally evaluating a lambda body first. None of this means lambdas are wrong — for a one-off transformation that doesn't match any of operator's named functions, a lambda is still the right tool, and operator's value is specifically in the common cases it names. The operator module documentation lists the full set, including in-place variants like iadd used internally by augmented assignment.