Skip to content

How to Get the Object with the Max Attribute Value in a List of Objects in Python

Learn how to efficiently find the object with the highest attribute value in Python using max() with lambda functions or attrgetter(), including handling edge cases like empty lists.

When working with lists of objects in Python, you'll often need to find the object that has the highest (or lowest) value for a specific attribute: for example, the employee with the highest salary, the product with the best rating, or the student with the top score. Python's built-in max() function makes this straightforward when combined with a key function.

In this guide, you'll learn multiple approaches to find the object with the maximum attribute value, along with explanations of how each method works and when to choose one over another.

Understanding the Problem

Given a list of objects, each with various attributes, the goal is to identify and return the entire object (not just the value) that has the maximum value for a specified attribute.

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

employees = [
    Employee("Alice", 75000),
    Employee("Bob", 60000),
    Employee("Charlie", 98000),
    Employee("Diana", 45000),
]

The simplest and most Pythonic approach uses max() with a lambda function as the key parameter:

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def __repr__(self):
        return f"Employee('{self.name}', {self.salary})"


employees = [
    Employee("Alice", 75000),
    Employee("Bob", 60000),
    Employee("Charlie", 98000),
    Employee("Diana", 45000),
]

top_earner = max(employees, key=lambda emp: emp.salary)

print(top_earner)
print(f"{top_earner.name} earns ${top_earner.salary:,}")

Output:

Employee('Charlie', 98000)
Charlie earns $98,000

How it works:

  1. max() iterates through the list of Employee objects.
  2. For each object, the lambda extracts the salary attribute.
  3. max() compares these extracted values and returns the original object with the highest salary.

Using max() with operator.attrgetter()

The attrgetter() function from the operator module creates a callable that retrieves a named attribute from an object. It's a clean alternative to lambda, especially when the attribute name is determined dynamically:

from operator import attrgetter

class Employee:
    def __init__(self, name, salary, experience):
        self.name = name
        self.salary = salary
        self.experience = experience

    def __repr__(self):
        return f"Employee('{self.name}', salary={self.salary}, exp={self.experience})"


employees = [
    Employee("Alice", 75000, 7),
    Employee("Bob", 60000, 3),
    Employee("Charlie", 98000, 5),
    Employee("Diana", 45000, 1),
]

top_salary = max(employees, key=attrgetter('salary'))
print(f"Highest salary: {top_salary}")

most_exp = max(employees, key=attrgetter('experience'))
print(f"Most experience: {most_exp}")

Output:

Highest salary: Employee('Charlie', salary=98000, exp=5)
Most experience: Employee('Alice', salary=75000, exp=7)

Dynamic Attribute Selection with attrgetter()

A key advantage of attrgetter() is that the attribute name can be stored in a variable, making it ideal for configurable or user-driven queries:

from operator import attrgetter

class Product:
    def __init__(self, name, price, rating):
        self.name = name
        self.price = price
        self.rating = rating

    def __repr__(self):
        return f"{self.name} (price={self.price}, rating={self.rating})"


products = [
    Product("Laptop", 999, 4.5),
    Product("Phone", 699, 4.8),
    Product("Tablet", 449, 4.2),
]

attribute = "rating"  # Could come from user input or config
best = max(products, key=attrgetter(attribute))

print(f"Best by {attribute}: {best}")

Output:

Best by rating: Phone (price=699, rating=4.8)

Finding Both Max and Min Objects

You can use the same pattern with min() to find the object with the lowest attribute value:

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary


employees = [
    Employee("Alice", 75000),
    Employee("Bob", 60000),
    Employee("Charlie", 98000),
    Employee("Diana", 45000),
]

highest = max(employees, key=lambda e: e.salary)
lowest = min(employees, key=lambda e: e.salary)

print(f"Highest salary: {highest.name} (${highest.salary:,})")
print(f"Lowest salary:  {lowest.name} (${lowest.salary:,})")

Output:

Highest salary: Charlie ($98,000)
Lowest salary:  Diana ($45,000)

Common Mistake: Calling max() on Objects Without a Key Function

If you call max() on a list of custom objects without specifying a key, Python does not know how to compare them and raises a TypeError.

Wrong approach

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary


employees = [Employee("Alice", 75000), Employee("Bob", 60000)]

top = max(employees)

Output:

TypeError: '>' not supported between instances of 'Employee' and 'Employee'

Correct approach: always provide a key

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary


employees = [Employee("Alice", 75000), Employee("Bob", 60000)]

top = max(employees, key=lambda e: e.salary)
print(f"{top.name}: ${top.salary:,}")

Output:

Alice: $75,000

Common Mistake: Empty List Causes ValueError

Calling max() on an empty list raises an error:

employees = []

top = max(employees, key=lambda e: e.salary)

Output:

ValueError: max() arg is an empty sequence

Correct approach: use the default parameter.

employees = []

top = max(employees, key=lambda e: e.salary, default=None)

if top is not None:
    print(f"Top earner: {top.name}")
else:
    print("No employees found.")

Output:

No employees found.

Sorting Objects by Attribute

If you need the top N objects rather than just the single maximum, use sorted():

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary


employees = [
    Employee("Alice", 75000),
    Employee("Bob", 60000),
    Employee("Charlie", 98000),
    Employee("Diana", 45000),
    Employee("Eve", 82000),
]

top_3 = sorted(employees, key=lambda e: e.salary, reverse=True)[:3]

print("Top 3 earners:")
for emp in top_3:
    print(f"  {emp.name}: ${emp.salary:,}")

Output:

Top 3 earners:
  Charlie: $98,000
  Eve: $82,000
  Alice: $75,000

Creating a Reusable Utility Function

For production code, a reusable function with proper error handling keeps things clean:

from operator import attrgetter

def get_max_by_attr(objects, attribute, default=None):
    """Return the object with the maximum value for the given attribute.

    Args:
        objects: Iterable of objects to search.
        attribute: Name of the attribute to compare.
        default: Value to return if the iterable is empty.

    Returns:
        The object with the maximum attribute value, or default if empty.
    """
    return max(objects, key=attrgetter(attribute), default=default)


def get_min_by_attr(objects, attribute, default=None):
    """Return the object with the minimum value for the given attribute."""
    return min(objects, key=attrgetter(attribute), default=default)


class Student:
    def __init__(self, name, gpa):
        self.name = name
        self.gpa = gpa


students = [
    Student("Alice", 3.8),
    Student("Bob", 3.5),
    Student("Charlie", 3.95),
]

top_student = get_max_by_attr(students, "gpa")
print(f"Top GPA: {top_student.name} ({top_student.gpa})")

result = get_max_by_attr([], "gpa", default=None)
print(f"Empty list result: {result}")

Output:

Top GPA: Charlie (3.95)
Empty list result: None

Quick Comparison of Methods

Method Readability Flexibility Import Required Best For
max() + lambda ⭐⭐⭐ High ⭐⭐⭐ High None Most use cases (recommended)
max() + attrgetter() ⭐⭐⭐ High ⭐⭐⭐ High operator Dynamic attribute names
sorted() + indexing ⭐⭐ Medium ⭐⭐⭐ High None Top/bottom N objects

All methods have O(n) time complexity for max()/min() and O(n log n) for sorted().

Conclusion

Finding the object with the maximum attribute value in a list is straightforward in Python:

  • max() with a lambda is the most Pythonic and readable approach for most scenarios.
  • attrgetter() is ideal when the attribute name is determined dynamically at runtime.
  • Always provide a key parameter when comparing custom objects; Python cannot compare them by default.
  • Use the default parameter to safely handle empty lists without raising exceptions.
  • Use sorted() when you need the top or bottom N objects, not just the single maximum.
Navigation