Plate 82
itemgetter vs lambda: Sort Key Lab
Hands-on operator.itemgetter vs lambda sort-key lab: real ops/s for extract and list.sort on tuples, dicts, and objects, measured on Linux localhost (lab).
Aditya Challa4 min read
Intro — what this post promises
Sorting with key=: is operator.itemgetter / attrgetter worth it over a lambda? This lab times key extraction alone and full list.sort on tuples, dicts, and small objects on Linux localhost.
Related links:
- sorted vs heapq vs bisect localhost lab
- dataclass vs slots vs dict localhost lab
- set vs list membership localhost lab
- lru_cache hit vs miss localhost lab
- itertools vs python loops localhost lab
- array vs list ints localhost lab
- copy vs deepcopy localhost lab
- perf_counter vs time localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. N=50,000. Affiliates: 0. Sort arms copy the list each run so input order stays fair.
Verdict up front: extract — tuple itemgetter ~57M/s vs map(lambda…) ~36M (~1.61×). Sort gaps shrink: tuple itemgetter vs lambda ~1.11×; attrgetter vs lambda ~1.07×. Prefer itemgetter for clarity + a free few percent; Timsort dominates wall time.
Arms
| Extract / sort key | Target |
|---|---|
itemgetter(1) / itemgetter("score") | tuple / dict |
attrgetter("score") | object attribute |
methodcaller("get_score") | method |
lambda … | same fields |
bare index / .get / direct method | baselines |
Lab topology
Script: lab-evidence/59-itemgetter-vs-lambda-sort/results/run_lab.py.
Lead table — extract only (p50)
| Arm | ops/s | ns/op |
|---|---|---|
tuple index t[1] | 83,938,812 | 11.9 |
| tuple itemgetter | 57,314,670 | 17.4 |
dict .get | 43,528,678 | 23.0 |
| dict itemgetter | 40,708,988 | 24.6 |
| obj attrgetter | 39,801,439 | 25.1 |
| obj method direct | 36,710,800 | 27.2 |
| tuple lambda map | 35,605,308 | 28.1 |
| obj lambda | 34,340,541 | 29.1 |
| dict lambda | 29,378,532 | 34.0 |
| methodcaller | 21,734,028 | 46.0 |
Lead table — full sort (p50)
| Arm | elems/s | ns/elem | p50 ms |
|---|---|---|---|
| tuple itemgetter | 5,243,900 | 190.7 | 9.53 |
| obj attrgetter | 5,132,451 | 194.8 | 9.74 |
| dict itemgetter | 4,835,901 | 206.8 | 10.34 |
| obj lambda | 4,782,305 | 209.1 | 10.46 |
| tuple lambda | 4,717,879 | 212.0 | 10.60 |
| methodcaller sort | 4,664,542 | 214.4 | 10.72 |
| dict lambda | 4,551,209 | 219.7 | 10.99 |
Reading it
itemgetter/attrgetterare C callables — they beat a Pythonlambdaon the extract microbench (~1.6× / ~1.16×).- Sort is mostly Timsort — the key function still runs O(n) times, but the relative gap compresses to roughly 6–11% here.
- Bare
t[1]beats itemgetter on extract — itemgetter shines as a reusable key= object, not as a replacement for a one-line index in a comprehension. methodcallerwas the slowest extract path (~46 ns); fine for clarity, not a speed pick.
Pitfalls
- Optimizing
key=before profiling sort size — N=50k is ~10 ms either way. - Multi-field keys —
itemgetter(1, 2)still wins vs a tuple-building lambda (same pattern). - Mutating during sort — undefined; this lab copies first.
- Assuming lambda is “slow” in absolute terms — nanoseconds, not milliseconds, per call.
When to pick what
| Need | Prefer |
|---|---|
key= on tuples/dicts | operator.itemgetter |
key= on attributes | operator.attrgetter |
| One-off / complex expression | lambda |
| Call a method as key | method or methodcaller (clarity) |
Reproduce
Evidence: /workspace/lab-evidence/59-itemgetter-vs-lambda-sort/results/.
Closing
itemgetter is a small, real win. On this box extract tuple itemgetter ~1.6× a lambda map; full sort still ~1.11× / attrgetter ~1.07× over lambda. Use operator helpers for hot key= paths; don’t rewrite a 1 ms sort for a 5% key shave.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5; N=50000. extract tuple itemgetter 57.3M vs lambda map 35.6M (~1.61x). sort tuple itemgetter 5.24M vs lambda 4.72M (~1.11x). attrgetter vs lambda sort ~1.07x. Affiliates: 0. Evidence: lab-evidence/59-itemgetter-vs-lambda-sort/.
Related links
Plate 15
attrgetter vs getattr: Hot Loop Lab
Hands-on operator.attrgetter vs builtin getattr lab: real ops/s for extract and sort by attribute versus direct access, measured on Linux localhost only.
Observability & SRE · 30 Sept 2026
Plate 12
islice vs list Slice Windows: Localhost Lab
Hands-on itertools.islice vs list slice window lab: real ops/s taking ranges from sequences, measured on Linux localhost in this hands-on lab for SREs.
Observability & SRE · 1 Oct 2026
Plate 07
heapq.merge vs sorted(chain): Localhost Lab
Hands-on heapq.merge vs sorted(chain) multi-way merge: real records/s on pre-sorted lists, measured on Linux localhost today in this hands-on lab for SREs.
Observability & SRE · 1 Oct 2026