What does operator.itemgetter(2, 0) return when called on a sequence?
answer
- A factory that builds a fetcher
- The lookups happen later, not now
- Count of keys changes the return shape
- One key bare, several keys tupled
- Works through the operand's __getitem__
basics
~10 sIt returns the two-element tuple (row[2], row[0]), in the order the indices were given. With a single index, operator.itemgetter returns that one item itself, not a one-element tuple.
solid answer
~40 s`operator.itemgetter(2, 0)` builds a callable; calling it on `row` performs `row[2]` and `row[0]` and returns them as the tuple `(row[2], row[0])` — the indices come back in the order you passed them, not in sorted order. The one-index form is the special case: `itemgetter(2)(row)` returns `row[2]` bare, with no tuple wrapper. Nothing is looked up when the getter is constructed; the indices are just stored, and every lookup happens at call time through the operand's `__getitem__`. That means it works on anything subscriptable — tuples, lists, dicts (`itemgetter('plan')` fetches a key), strings — and it accepts negative indices and even a `slice` object. A missing index or key raises the operand's own error: `IndexError`, `KeyError` or `TypeError`.
code
pycon · 8 lines>>> from operator import itemgetter
>>> row = ('EU-14', 'active', 4200, 'monthly')
>>> itemgetter(2, 0)(row)
(4200, 'EU-14')
>>> itemgetter(2)(row)
4200
>>> itemgetter(-1)(row)
'monthly'go deeper
Recall the two shapes: one key gives you the item itself, two or more give you a tuple in the order you listed them. Be able to name the module it comes from and show a one-line call.
Explain that construction only stores the keys and every subscription happens at call time through __getitem__, which is why dicts, negative indices and slice objects all work and why a missing key raises the operand's own error.
Show judgement about when the factory stops paying: it can fetch but never compute or supply a fallback, so ragged records or any derived value belong in a named function instead of a clever getter.
Own the consistency argument — a codebase that reaches for these small C callables in hot fetch-only paths and plain named functions everywhere else is easier to read and profile than one where every extraction is an ad-hoc inline expression.
`operator.itemgetter` is a small factory in the standard library's `operator` module. You call it with one or more lookup keys and it hands back a **callable object** that, when later applied to an operand, performs those lookups. Two facts about that sentence carry almost every interview answer on this: - the lookups are described up front but **performed later**, and - the number of keys you pass changes the **shape of the result**. ## The construction step stores, it does not fetch `getter = itemgetter(2, 0)` touches no data. There is no sequence in scope yet; all the object holds is the tuple of keys `(2, 0)`. The work happens on `getter(row)`, and it happens through the operand's `__getitem__` — `itemgetter` performs an ordinary subscription, exactly what `row[2]` compiles to. Nothing is cached between calls, so the same getter can be reused across thousands of different operands. ## One key returns a bare value; several return a tuple - `itemgetter(2)(row)` evaluates to `row[2]` — the raw item, not `(row[2],)`. - Add a second key and the return type changes: `itemgetter(2, 0)(row)` is `(row[2], row[0])`. This asymmetry is the single most common stumble, because code written against the one-key form breaks the moment somebody adds a second field. The order is **positional, as written**: `itemgetter(2, 0)` puts index 2 first, and it will not reorder the pair for you. Calling `itemgetter()` with no keys at all is an error — it raises `TypeError`. ## Any subscriptable operand works Because the mechanism is just `__getitem__`, the keys need not be integers. - On a dict, `itemgetter('plan')` fetches the value under the key `'plan'`; - on a list or tuple you can use negative indices, so `itemgetter(-1)` reads the last element; - and you may pass a `slice` object, so `itemgetter(slice(1, 3))([10, 20, 30, 40])` returns `[20, 30]`. Mixing kinds in one getter is legal as long as the operand supports each key — for a dict, `itemgetter('id', 'amount')` returns both values as a tuple. What does **not** work is reaching for an attribute: `itemgetter('plan')` applied to an object that merely has a `.plan` attribute raises `TypeError`, because the object is not subscriptable. That job belongs to `operator.attrgetter`. The trap version of this is a `namedtuple`, which looks like it should accept `itemgetter('amount')` — it does not, because tuple indexing demands an integer; you index a namedtuple by position or fetch the field by attribute. ## Errors surface at call time and belong to the operand - A missing integer index on a list raises `IndexError`; - a missing dict key raises `KeyError`; - an unsupported key type raises `TypeError`. `itemgetter` adds no default and no swallowing — there is no third argument the way `getattr` has one. If some rows may be short or some dicts may be missing a key, you must handle that yourself, and that is often the honest reason to write an ordinary function instead. ## Why the module exists at all Everything above can be written as an inline function of one argument, so `itemgetter` has to earn its place. It does so in three ways. 1. It is **implemented in C** in CPython, so a call does not push a Python frame — over a long loop of lookups that is a real, if modest, saving. 2. It is **declarative**: `itemgetter(2, 0)` states which fields, in which order, with nothing else hiding in the body. 3. And the resulting object is an instance of a real, importable type that knows how to rebuild itself, so it can be **serialized and shipped elsewhere**, which an anonymous inline function cannot. The place you meet it most is the `key=` argument of `sorted`, `min`, `max` and `heapq` helpers, where the multi-key form gives you a compound ordering key in one expression. ## The object is stateless, and that is the point A getter holds nothing but its keys, so it is safe to build once at module level and reuse from anywhere, including from several threads, and building one per element in a loop is pure waste. Because the result of the multi-key form is an ordinary tuple: - it unpacks like one — `ident, amount = itemgetter(0, 2)(row)` reads cleanly — - and it compares like one, element by element from the left, which is what makes the multi-key form useful for ordering records at all. Two getters built from the same keys are distinct objects and do not compare equal, so treat them as opaque callables rather than as values to test for equality. ## When not to use it `itemgetter` can only fetch; it cannot compute. The moment you need `row[2] * 1.2`, a `.lower()` on the fetched value, or a fallback for a missing field, you are past what it expresses and should write a named function — readability wins, and the C-level saving was never the point.
- Does operator.itemgetter(1, 3) do anything at the moment it is constructed?No. Construction only stores the keys `1` and `3` on the new object; no operand exists yet, so no lookup can happen. Every subscription runs at call time, through the operand's `__getitem__`. That is why one getter can be built once and reused across many different rows, and why an out-of-range index only blows up when you finally call it on a short row rather than when you create it.
- What happens if you apply operator.itemgetter('amount') to an object that has an .amount attribute?It raises `TypeError`, because `itemgetter` performs a subscription and a plain object is not subscriptable. Attribute lookup is `operator.attrgetter`'s job. The same trap catches people with a `namedtuple`: it supports subscription, but only with integers, so `itemgetter('amount')` on one raises `TypeError` about tuple indices while `itemgetter(2)` works fine.
- How do you get a default instead of an exception when the key may be missing?You do not get one from `itemgetter` — it has no default parameter, and a missing key propagates the operand's own `KeyError`, `IndexError` or `TypeError`. Either normalise the data first so every record has the field, or drop the factory and write a small named function that does the lookup inside a `try` or uses `dict.get` with a fallback. Pretending the getter can express a fallback is the usual mistake.
It is an order slip rather than the goods: you write down which shelf positions you want, hand the slip to any warehouse later, and get back exactly those positions in the order you listed them.
saying these in an interview costs you the question
- Thinks itemgetter(0) returns a one-element tuple
- Says operator.itemgetter fetches attributes rather than items
- Believes the lookup happens when the getter is constructed
- Cannot say what several indices return
- Claims itemgetter works only on lists and tuples
- Expects a default value for a missing key