sorted() takes a key: a function that turns each item into the thing to compare. With it you can sort by any field, by several fields, and in mixed directions, without writing a comparison function.
By one field
orders = [("A-1", "Ada", 30), ("A-2", "Linus", 55), ("A-3", "Grace", 55)] by_total = sorted(orders, key=lambda o: o[2], reverse=True) print(by_total)
[('A-2', 'Linus', 55), ('A-3', 'Grace', 55), ('A-1', 'Ada', 30)]Linus and Grace both spent 55, and they stay in their original order. Python's sort is stable: ties never get shuffled.
operator.itemgetter does the same job as the lambda, reads well, and is slightly faster:
from operator import itemgetter orders = [("A-1", "Ada", 30), ("A-2", "Linus", 55), ("A-3", "Grace", 55)] print(sorted(orders, key=itemgetter(1)))
[('A-1', 'Ada', 30), ('A-3', 'Grace', 55), ('A-2', 'Linus', 55)]By two fields, in different directions
Return a tuple: Python compares the first item, then the second on a tie. Negate a number to reverse just that part:
orders = [("A-1", "Ada", 30), ("A-2", "Linus", 55), ("A-3", "Grace", 55)] ranked = sorted(orders, key=lambda o: (-o[2], o[1])) print(ranked)
[('A-3', 'Grace', 55), ('A-2', 'Linus', 55), ('A-1', 'Ada', 30)]Biggest total first, then names A to Z among equal totals.
Why it works
key is called once per item, and the list is sorted by those results. That's why it's fast even when the key does real work, and why tuples work: they compare item by item.
When not to use it
You can't negate a string, so "names Z to A, then totals low to high" needs another route: sort twice, secondary field first, relying on stability (sort(key=total), then sort(key=name, reverse=True)). And remember list.sort() sorts in place and returns None; new = orders.sort() gives you None, not a list (why that bites).