Listening to Reuven M. Lerner's PyCon 2019 talk on decorators, and came across the following example of a decorating function.
import time
def once_per_minute(func):
"""A decorator that prevents the decorated func
from being invoked more than once per minute"""
last_invoked = 0
def wrapper(*args, **kwargs):
nonlocal last_invoked
elapsed_time = time.time() - last_invoked
if elapsed_time < 60:
raise Exception(f"Only {elapsed_time} has passed")
last_invoked = time.time()
return func(*args, **kwargs)
return wrapper
What caused confusion for me was, why does the last_invoked value persists with every run of the decorated function? I would assume that with every run of the decorated function, the decorator would be invoked. And every time the decorator is invoked, last_invoked should be reset to zero.
I looked into the documentation of the nonlocal keyword, and it seems to indicate that this is a variable that is bound in the nearest enclosing scope. That still does not explain why last_invoked is able to successfully track elapsed time.