Search code examples
pythonpython-decorators

Can't understand how parameters get fetched inside the wrapper function inside a decorator


I have this code:

class User:
    def __init__(self, name):
        self.name = name
        self.is_logged_in = False

def is_authenticated_decorator(function):
    def wrapper(*args, **kwargs):
        if args[0].is_logged_in == True:
            function(args[0])
    return wrapper

@is_authenticated_decorator
def create_blog_post(user):
    print(f"This is {user.name}'s new blog post.")

new_user = User("angela")
new_user.is_logged_in = True
create_blog_post(new_user)

I don't understand how the parameter of the create_blog_post(user) gets fetched inside the inputs of the wrapper.

I understand that the

@is_authenticated_decorator
def create_blog_post(user):

Is equivalent to

create_blog_post = is_authenticated_decorator(create_blog_post)

I understand that create_blog_post becomes the wrapper

I understand that create_blog_post(user) calls wrapper(user).

I just don't get how?!

Can someone explain the route/path that user parameters is taking to end up in the wrapper function? thanks your answer will be highly appreciated.


Solution

  • Indeed:

    The decoration comes down to create_blog_post = is_authenticated_decorator(create_blog_post)

    That means we call that decorator which returns wrapper, but with a specific binding of its function parameter to our original create_blog_post. To make it easier, let's call the original create_blog_post as original_create_blog_post. So the return statement in is_authenticated_decorator returns this function:

    def wrapper(*args, **kwargs):
        if args[0].is_logged_in == True:
            original_create_blog_post(args[0])
    

    (function has been replaced with orginal_create_blog_post as that was the value we provided for the function parameter)

    By assigning that returned value back to create_blog_post we have thus defined it as:

    def create_blog_post(*args, **kwargs):
        if args[0].is_logged_in == True:
            original_create_blog_post(args[0])
    

    And if we expand the definition of what I call original_create_blog_post, we get:

    def create_blog_post(*args, **kwargs):
        if args[0].is_logged_in == True:
            print(f"This is {args[0].name}'s new blog post.")
    

    Now when we call create_blog_post(user) the variable args[0] is user.