attrgetter vs itemgetter: Cleaner Data Access in Python
Learn the difference between operator.itemgetter and operator.attrgetter, when to use each for sorting and accessing data, and how they perform compared to lambdas.
Python's attrgetter vs itemgetter: When to Use Each for Cleaner Data Access
You're working with a list of dictionaries representing employees, and you need to sort them by salary. You could write a lambda, but there's a cleaner way. Python's operator module gives us two powerful tools: itemgetter and attrgetter. They look similar, but they solve different problems.
Let me break down exactly when and why you'd choose one over the other.
The Core Difference
itemgetter works on sequences and mappings (lists, tuples, dictionaries). attrgetter works on objects with attributes.
Here's the simplest way to think about it:
- Use
itemgetterwhen you access data with square brackets:my_dict['key']ormy_list[0] - Use
attrgetterwhen you access data with dot notation:my_object.attribute
itemgetter in Action
When you're dealing with dictionary data, itemgetter is your friend:
from operator import itemgetter
employees = [
{'name': 'Alice', 'salary': 85000, 'department': 'Engineering'},
{'name': 'Bob', 'salary': 72000, 'department': 'Marketing'},
{'name': 'Charlie', 'salary': 95000, 'department': 'Engineering'}
]
# Sort by salary
sorted_employees = sorted(employees, key=itemgetter('salary'))
It also shines with multiple keys:
# Sort by department, then salary
sorted_employees = sorted(employees, key=itemgetter('department', 'salary'))
And here's where PythonSkillset users often get surprised — itemgetter works on tuples and lists too:
from operator import itemgetter
data = [(3, 'apple'), (1, 'banana'), (2, 'cherry')]
sorted_data = sorted(data, key=itemgetter(0)) # Sorts by first element
attrgetter for Object Attributes
Now imagine you're working with class instances instead of dictionaries:
from operator import attrgetter
class Employee:
def __init__(self, name, salary, department):
self.name = name
self.salary = salary
self.department = department
def __repr__(self):
return f"{self.name} ({self.salary})"
employees = [
Employee('Alice', 85000, 'Engineering'),
Employee('Bob', 72000, 'Marketing'),
Employee('Charlie', 95000, 'Engineering')
]
# Sort by salary attribute
sorted_employees = sorted(employees, key=attrgetter('salary'))
Notice the difference? With attrgetter, you pass the attribute name as a string. With itemgetter, you pass the dictionary key.
The Chained Attributes Trick
Here's something that makes attrgetter especially useful:
# When you have nested objects
class Address:
def __init__(self, city):
self.city = city
class Person:
def __init__(self, name, address):
self.name = name
self.address = address
people = [
Person('Alice', Address('New York')),
Person('Bob', Address('Austin')),
Person('Charlie', Address('Denver'))
]
# Sort by city through the address attribute
sorted_people = sorted(people, key=attrgetter('address.city'))
Try doing that with a lambda without making your code look messy.
Performance Comparison
Both functions are implemented in C, making them faster than lambda in most cases:
from operator import itemgetter, attrgetter
import timeit
# With dictionaries
data = [{'a': i, 'b': i*2} for i in range(1000)]
lambda_time = timeit.timeit(
'sorted(data, key=lambda x: x["a"])',
globals={'data': data},
number=10000
)
itemgetter_time = timeit.timeit(
'sorted(data, key=itemgetter("a"))',
globals={'data': data, 'itemgetter': itemgetter},
number=10000
)
On Python 3.11, itemgetter typically runs about 10-20% faster than an equivalent lambda.
When NOT to Use Them
itemgetter and attrgetter aren't always the right choice. Skip them when:
- You need to transform data — If you need
abs(x)or string manipulation, lambda is clearer - You're accessing nested dictionary keys —
itemgetterworks on one level. Fordata['user']['name'], a lambda is simpler - Readability takes a hit — Sometimes a simple lambda is more obvious to your teammates
Real-World PythonSkillset Example
At PythonSkillset.com, we process user submission data regularly. Here's how we use both:
from operator import itemgetter, attrgetter
# itemgetter for raw API responses (dictionaries)
api_users = [
{'username': 'python_dev', 'score': 87, 'articles': 12},
{'username': 'data_diver', 'score': 92, 'articles': 8},
]
top_scorers = sorted(api_users, key=itemgetter('score'), reverse=True)[:3]
# attrgetter for our internal User objects
class User:
def __init__(self, username, score, articles):
self.username = username
self.score = score
self.articles = articles
users = [User(u['username'], u['score'], u['articles']) for u in api_users]
top_users = sorted(users, key=attrgetter('score'), reverse=True)
Quick Decision Guide
| You have... | Use... | Example key |
|---|---|---|
| List of dictionaries | itemgetter |
itemgetter('price') |
| List of tuples | itemgetter |
itemgetter(1) |
| List of objects | attrgetter |
attrgetter('price') |
| Nested attributes | attrgetter |
attrgetter('address.city') |
Both functions make your sorting and grouping code faster and cleaner. The next time you reach for a lambda to access a key or attribute, ask yourself: can itemgetter or attrgetter do this with less code and better performance?
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.