ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 61

  1. Blog
  2. /Observability & SRE

methodcaller vs getattr Call: Localhost Lab

Hands-on operator.methodcaller vs getattr vs direct: real calls/s invoking methods on many objects, measured on Linux localhost today in this lab for SREs.

Aditya Challa·30 September 2026·4 min read

Lab
On this page
  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — n=10 000 (p50 Mcalls/s)
  5. With argument (`add(1)`, n=10 000)
  6. Scale check (value())
  7. map/sorted fit
  8. Reading it
  9. Pitfalls
  10. Reproduce
  11. Limits
  12. Takeaway

Intro — what this post promises

Call a method on many objects: operator.methodcaller, getattr(obj, name)(), direct obj.method(), and pre-bound methods. This lab reports calls/s on Linux localhost.

It is not attrgetter (lab 65) or itemgetter (lab 59) — those fetch attributes/items; this invokes methods.

Related links:

  • attrgetter vs getattr localhost lab
  • itemgetter vs lambda sort localhost lab
  • functools partial vs lambda localhost lab
  • groupby vs manual localhost lab
  • hmac compare digest localhost lab
  • argparse vs sys argv localhost lab
  • futures as completed vs wait localhost lab
  • fnmatch vs re localhost lab

Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. No Docker. Widget.value() / Widget.add(1).

Verdict up front (10 000 objects): direct value() ~30.38 Mcalls/s; getattr ~17.04; methodcaller ~13.53; prebound calls ~29.68. Use methodcaller for clean map/sorted key hooks; direct calls when the name is fixed in code.


Arms

ArmPattern
directw.value()
methodcaller("value")reusable caller
getattr(w, "value")()dynamic name
bind-each-time[w.value for w in …] then call
preboundbind once, call many
add(1) variantsmethodcaller with args

Lab topology

n in {1000, 10000, 50000} Widget objects · 7 rounds · p50
metric: calls/s = n / p50_s
methods: value() no-arg · add(1) with fixed arg

Script: lab-evidence/99-methodcaller-vs-getattr/results/run_lab.py.


Lead table — n=10 000 (p50 Mcalls/s)

ArmMcalls/s
direct value()30.38
prebound then call29.68
getattr + call17.04
methodcaller value13.53
bind-each + call12.48

Direct and prebound sit near the top. methodcaller pays for a portable callable. Binding a fresh method object every outer loop (bound_methods) is the costly anti-pattern.


With argument (add(1), n=10 000)

ArmMcalls/s
direct22.44
methodcaller("add", 1)16.87
getattr + call14.24

With a bound argument, methodcaller closes some of the gap vs getattr — still behind a direct call on this box.


Scale check (value())

ndirectmethodcallergetattrprebound
1 00019.3510.6117.2729.39
10 00030.3813.5317.0429.68
50 00029.5619.5615.6228.89

Absolute rates move with cache effects; the ordering (direct / prebound ahead of dynamic lookup) is the useful takeaway.


map/sorted fit

methodcaller shines when an API wants a one-argument callable: sorted(objs, key=methodcaller("score")) or map(methodcaller("strip"), lines). That clarity often matters more than a few Mcalls/s on a microbench. Pair with attrgetter only when you need the attribute itself, not a call.


Reading it

  • Direct calls win when the method name is static.
  • methodcaller is for higher-order tools (map, sorted(..., key=methodcaller("lower"))) — readable, slight tax.
  • getattr+call suits truly dynamic names; expect overhead vs direct.
  • Pre-binding helps if you call the same bound methods repeatedly; binding every iteration loses.

Pitfalls

  • Using methodcaller where a direct call is fine (noise in profiles).
  • Binding methods in a tight loop without reuse.
  • Confusing attrgetter (get attribute) with methodcaller (call method).
  • Passing wrong arity into methodcaller-bound args.
  • Measuring only n=1k — check a larger size before drawing product conclusions.

Reproduce

python3 lab-evidence/99-methodcaller-vs-getattr/results/run_lab.py

Evidence: summary.json, summary.txt.


Limits

One Linux box. Microbench only — real workloads with I/O will dwarf call overhead. Tiny methods returning ints. Not free-threaded surprises. Not slots vs dict object layouts beyond Widget slots.


Takeaway

At 10 k objects, direct ~30.38 Mcalls/s beat getattr ~17.04 and methodcaller ~13.53. Prefer direct in hot loops; methodcaller when you need a portable callable for APIs that expect a function.

operator.methodcallergetattr callmethod invokebound methodpython operatorlocalhost labsrecalls/s

Lab evidence

What I found running this

Lab 1 Oct 2026 IST. Python 3.13.5. n=10k value(): direct 30.38 Mcalls/s; getattr 17.04; methodcaller 13.53; prebound 29.68. Not attrgetter/itemgetter. Affiliates: 0. Evidence: lab-evidence/99-methodcaller-vs-getattr/.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Related links

  • 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

  • Plate 88

    mmap Write vs pwrite Region: Localhost Lab

    Hands-on mmap MAP_SHARED write+msync vs pwrite region update: real MB/s with durability labels, measured on Linux localhost in this hands-on lab for SREs.

    Observability & SRE · 1 Oct 2026

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — n=10 000 (p50 Mcalls/s)
  5. With argument (`add(1)`, n=10 000)
  6. Scale check (value())
  7. map/sorted fit
  8. Reading it
  9. Pitfalls
  10. Reproduce
  11. Limits
  12. Takeaway
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove