Item 9: Function Decorator

from functools import wraps

def trace(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        a = ', '.join((repr(x) for x in args))
        print(func.__name__ + '(' + a + ')')
        return func(*args, **kwargs)
    return wrapper

def fac1(n):
    return 1 if n == 0 else n * fac1(n - 1)
fac1 = trace(fac1)

@trace
def fac2(n):
    return 1 if n == 0 else n * fac2(n - 1)

print(fac1(3))
print(fac2(3))