skip to content

What does operator.methodcaller('rjust', 8, '0') do when applied to a string?

level: middleimportance: nice to knowfreq 22%

answer

  1. The one that actually invokes something
  2. A method named by string, not by reference
  3. Extra arguments captured once, up front
  4. Receiver supplies the method each call
  5. Sibling fetches it; this one calls it

basics

~20 s

It returns a callable that runs s.rjust(8, '0') on whatever string you pass it, so '42' becomes '00000042'. The method name and its arguments are fixed when the methodcaller is built; the method itself is looked up on each operand at call time.

solid answer

~40 s

`operator.methodcaller('rjust', 8, '0')` builds a callable `f` such that `f(s)` is `s.rjust(8, '0')` — it calls a method **by name** and returns the result. The name and the extra arguments are captured once, when the `methodcaller` is constructed, and reused on every call; keyword arguments work too, as in `methodcaller('sort', reverse=True)`. Method resolution, by contrast, happens per operand: the object you pass supplies the method, so the same caller works across unrelated types that each define `rjust` — ordinary duck typing. The distinction against its siblings is worth stating: `operator.attrgetter('rjust')` hands you the bound method **without calling it**, `operator.itemgetter` subscripts, and `methodcaller` is the only one of the three that actually invokes something. One caution: the captured arguments are shared objects, so a mutable argument is the same object on every call.

code

pycon · 6 lines
pycon
>>> from operator import methodcaller
>>> pad = methodcaller('rjust', 8, '0')
>>> pad('42')
'00000042'
>>> list(map(methodcaller('get', 'id', -1), [{'id': 3}, {}]))
[3, -1]

go deeper

for a junior

Recall the shape: a method name as a string, then the arguments that method should receive, producing a callable you apply to an object. Say what it returns for a simple string method.

for a middle

Explain that name and arguments are captured at construction while the method is resolved on each operand, that keyword arguments are supported, and how it differs from attrgetter, which fetches the bound method rather than calling it.

for a senior

Bring up the sharp edges you have actually hit: a mutable captured argument shared across every call, a typo that only fails on first use, and in-place methods faithfully returning None.

for a principal

Own the readability tradeoff — a stringly-typed method name defeats static analysis and rename refactors, so justify it by the object's portability rather than reaching for it as a default style.

`operator.methodcaller` completes the trio in the `operator` module. - `itemgetter` subscripts, - `attrgetter` reads attributes, - and `methodcaller` **calls a method by name and returns its result**. `methodcaller('rjust', 8, '0')` produces a callable that, applied to `'42'`, evaluates `'42'.rjust(8, '0')` and returns `'00000042'`. ## What is fixed and what is late The first argument is the method's name as a string; everything after it — positional and keyword alike — is stored and passed through on each call. All of that is captured once, at construction. What is deliberately *not* fixed is the receiver, and therefore the method itself: the operand you pass in supplies it. `methodcaller('rjust', 8, '0')` does not point at `str.rjust`; it points at *whatever* `rjust` the operand happens to expose. That makes it polymorphic in the ordinary Python way — the same caller applied to instances of two unrelated classes calls each class's own implementation, which is exactly what you want when mapping one operation over a heterogeneous collection, and exactly what makes it fail with `AttributeError` the moment an element lacks the method. ## Keyword arguments are supported - `methodcaller('sort', reverse=True)` applied to a list calls `lst.sort(reverse=True)` in place — and returns `None`, because that is what `list.sort` returns. - `methodcaller('get', 'id', -1)` applied to a dict calls `d.get('id', -1)`, giving you a defaulted lookup that `itemgetter` cannot express. That combination — a named method plus fixed arguments — is the whole value proposition. ## The captured arguments are shared Because the arguments are stored once on the object, a mutable one is the *same object* every call. A caller built as `methodcaller('extend', buf)` passes the identical `buf` list to every operand, and mutations to it are visible everywhere. This is the same class of surprise as any value captured once and reused, and it is the reason to keep the bound arguments immutable — numbers, strings, tuples — unless sharing is genuinely what you mean. ## Against attrgetter, and against functools.partial Two contrasts pin the concept down. - **Against `attrgetter`.** `attrgetter('strip')(s)` returns the **bound method object** `s.strip`, uncalled; you would still have to invoke it. `methodcaller('strip')(s)` returns the **stripped string**. Confusing the two produces the classic bug where a collection is sorted by a bunch of method objects — which are not comparable — instead of by their results. - **Against `functools.partial`:** `partial` binds arguments to a **specific function object** you already have in hand, so `partial(str.rjust, width=8)` is tied to `str.rjust` and to nothing else, whereas `methodcaller` binds a **name** that is resolved on each operand. `partial` fixes the callee and leaves the receiver free only if you pass the unbound function explicitly; `methodcaller` is the version that reads naturally when the receiver is the thing that varies. ## Where it shows up Anywhere a small one-argument callable is required and the operation is 'call this method on it': the `key=` argument of sorting helpers, a mapping over a sequence, a callback slot, a table of operations. It is genuinely the least-used of the three factories, because 'call a method with fixed arguments' is a mouthful in string form and an inline function often reads better — `methodcaller('rjust', 8, '0')` is not obviously clearer than writing the call out. Its edge appears when the callable must be **stored, compared or shipped**: a `methodcaller` is an instance of a real importable type that can be serialized, so it survives being sent to a worker process or held in a configuration table, which an anonymous inline function does not. ## Failure modes to name in an interview 1. A missing method raises `AttributeError` when the caller is applied, not when it is built — nothing is resolved at construction, so a typo in the method name lies dormant until the first call. 2. Wrong arguments raise the ordinary `TypeError` from the method itself. 3. And returning `None` from an in-place method such as `list.sort` or `list.append` catches people who expect a fluent, chainable result — the caller returns whatever the method returned, faithfully including `None`. ## The judgement call Reach for `methodcaller` when you need a reusable, serializable, declarative 'call this named method' object. Write the call out longhand when it is used once, in one place, and clarity matters more than the object's portability.

  • When is the method name resolved — at construction or at call time?
    At call time, on the operand. The `methodcaller` stores only the name string, so a typo or a missing method raises `AttributeError` the first time the caller is applied, never when it is built. The upside is polymorphism: one caller works across unrelated types that each define the method, and each object's own implementation runs.
  • What is the risk in operator.methodcaller('extend', buffer) where buffer is a list?
    The list is captured once and passed to every operand, so all calls share one object and any mutation of it is visible to all of them. Bound arguments are stored on the caller, not re-evaluated per call. Keep captured arguments immutable — numbers, strings, tuples — unless the sharing is deliberate, and build a fresh caller when you need fresh state.
  • How does operator.methodcaller differ from functools.partial?
    `partial` binds arguments to a specific function object you already hold, so the callee is fixed and only the remaining arguments vary. `methodcaller` binds a method *name*, resolved on whichever operand it is applied to, so the callee varies with the receiver. Use `partial` when the function is the constant, `methodcaller` when the receiver is the variable and the operation is 'call this named method on it'.

saying these in an interview costs you the question

  • Thinks methodcaller returns the method without calling it
  • Believes the bound arguments are re-evaluated each call
  • Says methodcaller accepts only positional arguments
  • Assumes the method is resolved against one fixed class
  • Expects a missing method to fail at construction time
  • Expects a return value from an in-place method call

context