How can a decorator still call the function it was given after the decorator has returned? It returns an inner function that retains access to that function. This inner function is a closure: it uses a variable from its enclosing function’s scope, even after that outer call has finished. section 7.3.2
A decorator that creates a closure
def monitor(func): def monitored(): print(f"*** {func.__name__} is called") func() return monitored @monitor def example(): pass example() # *** example is called Calling monitor creates and returns monitored. When example() runs, that returned function can still access func, the original example function. section 7.3.2
A wrapper that accepts no arguments fails when the decorated function needs one. For a reusable decorator, let the inner function accept *args and **kwargs, pass them to the original function, and return its result. Use functools.wraps(func) on the inner function to preserve the original function’s __name__ and __doc__; otherwise, those attributes describe the wrapper instead. section 7.3.2 section 7.3.3
>>> closure.__closure__[0].cell_contents 100 >>> closure.__closure__[1].cell_contents 5
Inspecting __closure__ shows the values bound by an inner function created through outer(100): 100 and 5. This is a useful check of what that particular closure retains, not a step required to use decorators. section 7.3.2



















