I think your intuition is about right.
Historically, callable objects (or what I’ve sometimes heard called “functors”) have been used in the OO world to simulate closures. In C++ they’re frequently indispensable.
However, __call__ has quite a bit of competition in the Python world:
- A regular named method, whose behavior can sometimes be a lot more easily deduced from the name. Can convert to a bound method, which can be called like a function.
- A closure, obtained by returning a function that’s defined in a nested block.
- A lambda, which is a limited but quick way of making a closure.
- Generators and coroutines, whose bodies hold accumulated state much like a functor can.
I’d say the time to use __call__ is when you’re not better served by one of the options above. Check the following criteria, perhaps:
- Your object has state.
- There is a clear “primary” behavior for your class that’s kind of silly to name. E.g. if you find yourself writing
run()ordoStuff()orgo()or the ever-popular and ever-redundantdoRun(), you may have a candidate. - Your object has state that exceeds what would be expected of a generator function.
- Your object wraps, emulates, or abstracts the concept of a function.
- Your object has other auxilliary methods that conceptually belong with your primary behavior.
One example I like is UI command objects. Designed so that their primary task is to execute the comnand, but with extra methods to control their display as a menu item, for example, this seems to me to be the sort of thing you’d still want a callable object for.