Key Functions, lambda, itemgetter and attrgetter
18 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Real data is rarely a plain list of numbers. Orders are tuples, employees are dictionaries or objects. The key argument tells Python which part of each record to sort by.
A key function decides the order
Key function is a function you pass as key=. Python calls it once per item and sorts by the values it returns, while the items in the result stay unchanged.
Product names sort with capitals first, which looks wrong to a customer. A key that lowercases each name fixes the order.
products = ["keyboard", "Monitor", "cable", "Dock"]print(sorted(products))print(sorted(products, key=str.lower))Example 1 · Sort by distance from zero
adjustments = [-120, 45, -30, 200]print(sorted(adjustments, key=abs))[-30, 45, -120, 200]
What this code does
absis a built-in function, so it can be a key directly.- The values are compared as 30, 45, 120, 200, but the original values are returned.
str.lower has no brackets. You pass the function itself, and Python calls it once for each item.
lambda for a one-off key
lambda is a one-line anonymous function written as lambda arguments: expression. It returns the value of the expression. Use it for short keys that are needed in one place.
Orders are tuples of id and amount. To sort by amount you need the second item. A lambda writes that rule in one line.
orders = [("A-1", 250), ("A-2", 40), ("A-3", 900)]print(sorted(orders, key=lambda order: order[1]))Example 2 · Sort invoice names by the number in them
files = ["inv-10", "inv-2", "inv-33"]print(sorted(files))print(sorted(files, key=lambda name: int(name.split("-")[1])))['inv-10', 'inv-2', 'inv-33'] ['inv-2', 'inv-10', 'inv-33']
What this code does
- Plain text sorting puts inv-10 before inv-2, because the character 1 comes before 2.
- The lambda cuts out the number and turns it into an int, so 2 sorts before 10.
lambda order: order[1] is a small function that takes one order and returns its amount.
itemgetter for tuples, lists and dictionaries
operator.itemgetter builds a function that returns one item, or several, from whatever you give it. It works with tuples, lists and dictionaries and replaces lambda x: x[i].
Rows loaded from a file are often dictionaries. itemgetter builds the key function for you and reads clearly.
from operator import itemgetter
rows = [ {"id": "E1", "dept": "Sales", "pay": 52000}, {"id": "E2", "dept": "IT", "pay": 61000}, {"id": "E3", "dept": "Sales", "pay": 47000},]print([row["id"] for row in sorted(rows, key=itemgetter("pay"))])Example 3 · itemgetter with two positions
from operator import itemgetter
students = [("Asha", "B", 88), ("Ravi", "A", 72), ("Meena", "B", 95), ("Diya", "A", 72)]ranked = sorted(students, key=itemgetter(1, 2))print([name for name, *_ in ranked])['Ravi', 'Diya', 'Asha', 'Meena']
What this code does
itemgetter(1, 2)returns the tuple (class, marks) for each student.- Ravi and Diya tie on both, so stability keeps Ravi first.
itemgetter("pay") does the same as lambda row: row["pay"] and is a little faster.
attrgetter for objects
operator.attrgetter builds a function that reads one or more attributes from an object. It is the object version of itemgetter and replaces lambda e: e.name.
Employees stored as objects have attributes, not keys. attrgetter reads an attribute by name.
from dataclasses import dataclassfrom operator import attrgetter
@dataclassclass Employee: name: str dept: str pay: int
staff = [Employee("Asha", "Sales", 52000), Employee("Ravi", "IT", 61000), Employee("Meena", "Sales", 47000)]print([e.name for e in sorted(staff, key=attrgetter("pay"))])attrgetter("pay") is the same as lambda e: e.pay. It also accepts several names.
Sorting by several fields
A payroll report lists departments in order and, inside each department, the highest pay first. Return a tuple from the key.
print([e.name for e in sorted(staff, key=lambda e: (e.dept, -e.pay))])Example 4 · Department ascending, pay descending, by two passes
from operator import itemgetter
staff = [("Asha", "Sales", 52000), ("Ravi", "IT", 61000), ("Meena", "Sales", 47000), ("Kiran", "IT", 58000), ("Diya", "Sales", 52000)]step1 = sorted(staff, key=itemgetter(2), reverse=True)step2 = sorted(step1, key=itemgetter(1))print([name for name, *_ in step2])['Ravi', 'Kiran', 'Asha', 'Diya', 'Meena']
What this code does
- Sort by the less important field first, then by the more important one.
- The second sort leaves ties alone, so pay order survives inside each department. Asha and Diya tie and keep arrival order.
Tuples compare the first item, then the second on a tie. The minus sign flips numbers only. It cannot flip text.
Next step
Check your understanding
12 quick questions