Callable Objects#
Python's callable protocol means anything with __call__ can be invoked like a function. Functions are one kind of callable — classes, bound methods, functools.partial, and custom objects can all implement __call__.
How to use this page
Connects functions, classes, and typing. See Higher Order Functions, Dunder or Magic Methods.
At a glance
| Track | Python Advanced → Advanced Functions |
| Sections | 8 major topics |
| Outline | Use the right-hand TOC to jump |
Topics: What is callable? · Kinds of callables · Functor pattern — stateful callable object · Classes as callables · __call__ with decorators · Typing callables · operator module callables · Choosing: function vs functor vs partial
- What is callable?
- Kinds of callables
- Functor pattern — stateful callable object
- Classes as callables
__call__with decorators- Typing callables
operatormodule callables- Choosing: function vs functor vs partial
What is callable?#
Objects implementing __call__ return True for callable().
Kinds of callables#
| Type | Example | Notes |
|---|---|---|
| Function | def f(): ... |
__call__ on function type |
| Bound method | obj.method |
self bound |
| Class | MyClass() |
__call__ on metaclass for construction |
Instance with __call__ |
obj() |
Functor pattern |
partial |
partial(fn, 1) |
Pre-filled function |
lambda |
lambda x: x |
Anonymous function |
| Built-in | len, int |
C or Python builtin |
Functor pattern — stateful callable object#
class Accumulator:
def __init__(self):
self.total = 0
def __call__(self, x: int) -> int:
self.total += x
return self.total
acc = Accumulator()
acc(5) # 5
acc(3) # 8
Compare to closure counter in Closures — functor when you want named type, multiple methods, or inheritance.
Classes as callables#
Calling a class invokes __init__ after object creation:
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
p = Point(1, 2) # Point.__new__ then Point.__init__
Metaclasses customize class call — see Metaclasses.
__call__ with decorators#
Class-based decorators implement __call__ to wrap functions:
class CountCalls:
def __init__(self, fn):
self.fn = fn
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
return self.fn(*args, **kwargs)
@CountCalls
def work():
print("working")
Typing callables#
from collections.abc import Callable
Handler = Callable[[str, int], bool]
def register(h: Handler) -> None:
...
# Callable with any args
Factory = Callable[..., object]
typing.Protocol for structural callables:
See Type Hints and Annotations.
operator module callables#
from operator import add, itemgetter, attrgetter
add(2, 3) # 5
itemgetter(1)([10, 20, 30]) # 20
attrgetter("name")(obj)
Pre-built callables avoid lambdas.
Choosing: function vs functor vs partial#
| Need | Choose |
|---|---|
| Simple one-off | def |
| Fixed args to existing fn | partial |
| Stateful + multiple methods | Class with __call__ |
| Lightweight state | Closure |
| Pass to HOF / decorator | Any callable |
Interview traps (quick reference)#
| Trap | What goes wrong | Safe approach |
|---|---|---|
Confuse class call vs __call__ instance |
Wrong mental model | Class() creates instance |
| Stateful functor shared | Unintended shared state | One instance per context |
| Wrong Callable type args | Checker errors | Match signature in Callable[[...], R] |
Mental model checklist#
- What makes an object callable?
- How does a functor differ from a closure?
- What happens when you call a class?
- How do you type a function argument?
What's next#
| Topic | Page |
|---|---|
| Dunder methods | Dunder or Magic Methods |
| Partial functions | Partial Functions |
| Higher-order functions | Higher Order Functions |