3.2.8.1. User-defined functions
3.2.8.2. Instance methods
3.2.8.3. Generator functions
3.2.8.4. Coroutine functions
3.2.8.5. Asynchronous generator functions
3.2.8.6. Built-in functions
3.2.8.7. Built-in methods
3.2.8.8. Classes
3.2.8.9. Class Instances
Instances of arbitrary classes can be made callable
by defining a __call__() method in their class.
My concern is that all these callables have some special introspection-related attributes like func.__doc__, func.__annotations__except the last one is different and thatâs why this simple code is not correct:
def verbose_call(clb):
print(f"LOG: going to call {clb.__name__}")
clb()
It can fail to produce the log message, because class instances with __call__ do not have a __name__ attribute. I guess some deveplopers are not aware of this possible problem.
After special-casing the callable instance weâll get something like this:
if not isinstance(clb, type) and hasattr(type(clb), '__call__'):
# case 3.2.8.9 = instances with __call__ in their class
name = f"{type(clb).__name__}.__call__"
else:
name = clb.__name__
print(f"LOG: going to call {name}")
Iâd like to propose:
adding a function to the inspect module that returns âwhat will be calledâ. It returns the callable itself except in the mentioned special case it returns its .__call__ method. Update: also Class -> Class.__init__.
If this doesnât find support, I would find helpful if some information about this aspect of introspecting callables will be added to the docs.
stating which special attributes are common for all callables (I refer to the âwhat will be calledâ here). Something like that you can safely get __name__, __qualname__, __docs__, __module__, __annotations__ no matter what subtype of a callable you have.
Reading through this, Iâm not sure if I would want to always want to get the introspection values from the __call__ method rather than the class itself? When Iâm looking for a callableâs signature, itâs certainly the __call__'s that I care about. But the __name__ is always just going to be "__call__". I think that depending on the use case Iâd really rather want the classâs name, qualified name or even something specific to the instance Iâm calling itself. Similar things happen with __docs__, sometimes Iâm looking for the __call__ docs but other times I want the classâs docs.
Also, sometimes Pythonâs view of âwhat will be calledâ doesnât really line up with our conceptual view. Like, when you write SomeClass() you generally think of that calling SomeClass.__init__ and youâd want that methodâs introspected properties. But really, what Python is seeing is that itâll call type.__call__, which doesnât really have usefully introspectable attributes.
I think that this kind of introspection is too domain-specific to really be possible to generalize like this. You can definitely come up with reasonable behaviour for every possible case, but each such set of behaviours will only really be useful for one specific use case. Because of that, I think itâs better to have stuff like this in user code rather than the standard library, even if it is a bunch of tedious boilderplate.