import time def tracer(func): # Use function, not class with __call__ calls = 0 # Else self is decorator instance only def onCall(*args, **kwargs): nonlocal calls calls += 1 print(f'call {calls} to {func.__name__}') return func(*args, **kwargs) return onCall def timer(label='', trace=True): # On decorator args: retain args def onDecorator(func): # On @: retain decorated func def onCall(*args, **kargs): # On calls: call original start = time.perf_counter() # State is scopes + func attribute result = func(*args, **kargs) elapsed = time.perf_counter() - start onCall.alltime += elapsed if trace: funcname, alltime = func.__name__, onCall.alltime print(f'{label}{funcname}: {elapsed:.5f}, {alltime:.5f}') return result onCall.alltime = 0 return onCall return onDecorator