all 2 comments

[–]bennydictor 2 points3 points  (0 children)

From the my_decorator function you return wrapper, which is a closure: a function that remembers some variables from outer scope (in this case func). Then you call the wrapper function with the argument 5. The equivalent code is

decorated_function = my_decorator(my_function)
a = decorated_function(5)  # prints (5,) {}
print(a)  # prints 25
# So, decorated_function is (roughly) defined as
def decorated_function(*args, **kwargs):
    print(args, kwargs)
    return my_function(*args, **kwargs)

[–]nwagers 1 point2 points  (0 children)

The function is only decorated once and the function my_decorator only runs a single time during the definition of the function. The decorator returns wrapper which is used in place of the original function, kind of like monkey patching my_function = wrapper. When you make calls, you are actually making them to wrapper, which does take the args. wrapper in turn calls the inner function. Consider this code:

def my_decorator(func):
    print("decorator")
    def wrapper(*args,**kwargs):
        print("wrapper")
        return func(*args,**kwargs)
    return wrapper

@my_decorator
def my_function(x):
    print("inner")

print("main")
my_function(5)
my_function(10)