Short answer: both are fundamental, .__call__()
on functions is just a virtual trick.
The rest of this answer is a bit complicated. You don't have to understand it, but I find the subject interesting. Be warned that I'm going to present a series of lies, progressively fixing them.
Long answer
At the most fundamental level, Python can be said to have just 2 operations:
- attribute access:
obj.attr
- function call:
callable(args)
Method calls - obj.method(args)
- are not fundamental. They consist of 2 steps: fetching the attribute obj.method
(which gives a callable "bound method" object) and calling that with args
.
Other operators are defined in terms of them. E.g. x + y
tries x.__add__(y)
, falling back to other similar combinations if that doesn't work.
Infinitely Long Answer?
So far so good. But calling and attribute access themselves are also defined in terms of obj.__call__(args)
and obj.__getattribute__(name)
?!?
Is it turtles all the way down?!?
The trick is that operations on an object are defined by calling methods of its type: type(obj).__call__(obj, args)
and type(obj).__getattribute__(obj, name)
. Which BTW means that I lied to you, and there is a third fundamental operation:
- getting the type of an object:
type(obj)
OK, this is still not helpful. type(obj).__call__(...)
still involves an attribute access and a call, so this should continue ad infinitum? The rub is that eventually you hit a builtin type - usually a function, object
or type
- and for them attribute access and function calls are fundamental.
So when you call a instance of a custom class, that's implemented through its __call__
method indeed. But its __call__
method is probably a normal function - which can be called directly. End of mystery.
Similarly about __getattribute__
- you can provide it to define attribute access for your class, but the class itself implement attribute access fundamentally (unless it has a custom metaclass).
The Curtain in Front of the Man
So why does even a function has a fred.__call__
method? Well that's just smoke and mirrors that Python pulls to blur the difference between builtin types and custom classes. This method exists on all callable objects, but calling a normal function doesn't have to go through it - functions are fundamentally callable.
Similarly, all objects have obj.__getattribute__
and obj.__class__
, but for built-in types it just exposes the fundamental operations instead of defining it.
Small Print
The first claim that Python had 2 fundamental operations was actually a complete lie. Technically, all Python operators have a "fundamental" operation at the C level, exposed for consistency through a method, and custom classes can redefine these operations through similar methods.
But the story I told you could have been true, and it reduces the question its center: why .__call__()
and .__getattribute__()
are not an infinite recursion.