operator.attrgetter: Sort Objects by Attributes

Published on: September 2, 2026
Reading time: 4 minutes
Software developer organizing object data with Python operator.attrgetter

When Python code works with lists of objects, a frequent task is sorting, grouping, selecting, or transforming records according to an attribute. The usual solution is a lambda such as lambda item: item.name. It is valid and familiar, but the standard library offers a more declarative alternative: operator.attrgetter. It creates a callable dedicated to retrieving attributes, including nested paths and multiple fields.

This guide explains how Python operator.attrgetter works, how to use it with sorted, min, max, and itertools.groupby, how it differs from itemgetter, and when a lambda or named function remains a better choice.

What operator.attrgetter does

attrgetter lives in the operator module. You pass one or more attribute names and receive a callable.

from operator import attrgetter
get_name = attrgetter("name")
print(get_name(user))

The returned object can be passed directly as a key function. This makes the intention explicit: the key is an attribute lookup rather than an arbitrary computation.

Sorting objects by one attribute

from dataclasses import dataclass
from operator import attrgetter

@dataclass
class Product:
    name: str
    price: float
    stock: int

products = [
    Product("Keyboard", 180.0, 8),
    Product("Mouse", 90.0, 15),
    Product("Monitor", 1200.0, 4),
]

by_price = sorted(products, key=attrgetter("price"))

This is equivalent to sorted(products, key=lambda p: p.price). For a broader explanation of keys, stability, and reverse order, read the Python sort versus sorted guide.

The same getter works with min and max:

cheapest = min(products, key=attrgetter("price"))
most_expensive = max(products, key=attrgetter("price"))

Nested attribute paths

A major advantage is dotted access. If an order has a customer and that customer has an address, you can retrieve the city with a string path.

get_city = attrgetter("customer.address.city")
city = get_city(order)

This is concise and readable in report builders, serializers, table renderers, and data pipelines. However, every intermediate object must exist. If customer is None, Python raises AttributeError. A custom function is better when optional values require defaults.

Retrieving multiple attributes

Passing multiple names returns a tuple.

key = attrgetter("stock", "name")
ordered = sorted(products, key=key)

Python compares tuples from left to right, so records are ordered by stock first and name second. Stable sorting makes this behavior predictable. Multiple getters are useful in reports and deterministic exports.

Grouping with itertools.groupby

attrgetter pairs naturally with itertools.groupby. The data must first be sorted by the same key because groupby combines consecutive equal keys.

from itertools import groupby
from operator import attrgetter

ordered = sorted(products, key=attrgetter("stock"))
for stock, group in groupby(ordered, key=attrgetter("stock")):
    print(stock, list(group))

See the introduction to itertools for the lazy iterator model behind this pattern.

attrgetter versus itemgetter

Use attrgetter for object attributes such as user.name. Use itemgetter for indexes or mapping keys such as user["name"].

from operator import attrgetter, itemgetter
attrgetter("name")(user)
itemgetter("name")({"name": "Ana"})

methodcaller, another helper from operator, creates a callable that invokes a method. For example, methodcaller("strip") can normalize strings. Choosing the helper that matches the data contract makes code easier to understand.

When a lambda is better

attrgetter is designed for direct attribute retrieval. If the key performs arithmetic, normalization, validation, fallback handling, or conditional logic, use a lambda or named function.

ordered = sorted(products, key=lambda p: p.price * (1 - p.discount))

Named functions are preferable when logic deserves tests or documentation.

def safe_city(order):
    customer = getattr(order, "customer", None)
    address = getattr(customer, "address", None)
    return getattr(address, "city", "")

The built-in getattr accepts a default value. attrgetter does not. Review the Python functions guide for strategies to keep key functions focused.

Reusing getters

A getter can be stored once and reused across operations.

by_created_at = attrgetter("created_at")
ordered = sorted(records, key=by_created_at)
latest = max(records, key=by_created_at)

This avoids repeated strings and centralizes the rule. In larger applications, reusable getters may belong near models, report definitions, or query adapters.

Performance considerations

attrgetter may be slightly faster than an equivalent lambda in some workloads because it is implemented as an optimized standard-library helper. Still, benchmark before treating that difference as important. Readability and correctness matter more in most business applications. For systematic measurements, use the techniques described in the timeit guide.

Properties and descriptors

attrgetter performs normal Python attribute access. Therefore it can execute properties, descriptors, or custom __getattr__ logic. If a property performs database access, network calls, or expensive computation, sorting may evaluate it many times. Key functions should ideally be deterministic, inexpensive, and free of side effects.

Do not use attrgetter as static introspection. The inspect module provides specialized tools when descriptors must not run.

Error handling

An incorrect name raises AttributeError. Validate external configuration before creating getters from user-provided strings. If a set of fields is configurable, maintain an allowlist rather than accepting arbitrary paths. Tests should cover missing attributes, None intermediate values, and mixed object types.

Practical design guidelines

Use attrgetter when the operation is only attribute retrieval. Prefer a named getter variable when it is reused. Keep sort and group keys consistent. Avoid expensive properties in keys. Use tuples for deterministic multi-field ordering. Choose a regular function for fallbacks or business rules. Document dotted paths when object relationships are not obvious.

For authoritative details, consult the official operator.attrgetter documentation and the official Python sorting HOWTO.

Conclusion

Python operator.attrgetter is a small but valuable tool for declarative object processing. It handles simple attributes, nested paths, and multiple fields while integrating cleanly with sorting, selection, and grouping functions. It should not replace every lambda. Instead, use it where attribute lookup is the entire rule, and switch to an explicit function when validation, transformation, defaults, or side effects enter the picture.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Programmer analyzing code to identify file MIME types with Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: Detect MIME Types

    Learn Python mimetypes.guess_file_type for MIME detection in paths, URLs, uploads, and HTTP responses with safe fallbacks.

    Ler mais

    Tempo de leitura: 5 minutos
    13/09/2026
    Server components representing isolated Python interpreters running in parallel
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: True Parallelism in Python

    Learn Python InterpreterPoolExecutor for CPU-bound tasks, isolated interpreters, true parallelism, and safer concurrency design.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocessor representing CPUs available to a Python process
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: Count Available CPUs

    Learn Python os.process_cpu_count to size workers according to the CPUs actually available to the process.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026
    Laptop with digital code representing SQLite BLOB data
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    sqlite3.Blob: Incremental BLOB Reads and Writes

    Learn Python sqlite3.Blob for incremental BLOB reads and writes, lower memory use, and safer binary data handling in SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    12/09/2026
    Statistical analysis for Python random.binomialvariate
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    random.binomialvariate: Simulate Binomial Outcomes

    Learn Python random.binomialvariate to simulate successes, validate probabilities, and analyze binomial scenarios with practical examples.

    Ler mais

    Tempo de leitura: 5 minutos
    11/09/2026
    Python source code and function signature analysis
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    inspect.signature.bind: Validate Function Arguments

    Learn Python inspect.signature.bind to validate arguments, apply defaults, and build safer decorators and dynamic APIs.

    Ler mais

    Tempo de leitura: 4 minutos
    11/09/2026