A (python3) решение с использованием пользовательского метакласса:
from collections import defaultdict
from functools import wraps
import inspect
def count_calls(func):
name = func.__name__
@wraps(func)
def wrapper(self, *args, **kwargs):
# creates the instance counter if necessary
counter = getattr(self, "_calls_counter", None)
if counter is None:
counter = self._calls_counter = defaultdict(int)
counter[name] += 1
return func(self, *args, **kwargs)
wrapper._is_count_call_wrapper = True
return wrapper
class CallCounterType(type):
def __new__(cls, name, bases, attrs):
for name, attr in attrs.items():
if not inspect.isfunction(attr):
# this will weed out any callable that is not truly a function
# (including nested classes, classmethods and staticmethods)
continue
try:
argspec = inspect.getargspec(attr)
except TypeError:
# "unsupported callable" - can't think of any callable
# that might have made it's way until this point and not
# be supported by getargspec but well...
continue
if not argspec.args:
# no argument so it can't be an instancemethod
# (to be exact: a function designed to be used as instancemethod)
# Here again I wonder which function could be found here that
# doesn't take at least `self` but anyway...
continue
if getattr(attr, "_is_count_call_wrapper", False):
# not sure why we would have an already wrapped func here but etc...
continue
# ok, it's a proper function, it takes at least one positional arg,
# and it's not already been wrapped, we should be safe
attrs[name] = count_calls(attr)
return super(CallCounterType, cls).__new__(cls, name, bases, attrs)
class ParentClass(metaclass=CallCounterType):
pass
class ChildClass(ParentClass):
def child_method(self):
pass
Обратите внимание, что сохранение количества вызовов в экземпляре позволяет только считать вызовы методов экземпляра, очевидно ...