Search code examples
pythonfunctiondecoratorwrapper

Decorator vs Function without wrapper - Python


I am currently trying to understand what decorators are and what they are used for. Until now, I've learned that they are just functions which pass and return functions as arguments and their purpose is to modify a function w/o modifying the original function.

My question is related to the classic structure used to define them. Usually you define a decorator function and within it, another function called wrapper which is returned by the decorator function.

So my question is, why is wrapper created inside the decorator when it could be done with just a function? Would`t it be more "pythonic" to reduce code side by doing so?

As an example, the following 2 pieces of code:

def country(func):
    def wrapper_function():
        print("The name of my favorite country is: ")
        return func()
    return wrapper_function

@country # == fav_country = country(fav)
def fav():
    print("Sweden")

fav()

OUTPUT:

The name of my favorite country is:

Sweden

def country(func):
    print("The name of my favorite country is: ")
    return func

@country # == fav_country = country(fav)
def fav():
    print("Sweden")

fav()

OUTPUT: The name of my favorite country is:

Sweden


Solution

  • You are correct that a decorator is nothing else then a function taking a function as an argument and returning a function. But these two:

    def country1(func):
        def wrapper_function():
            print("The name of my favorite country is: ")
            return func()
        return wrapper_function
    
    def country2(func):
        print("The name of my favorite country is: ")
        return func
    

    are not equivalent. Consider

    @country1
    def fav1():
        print("Sweden")
    
    @country2
    def fav2():
        print("Sweden")
    

    and see what happens when you call

    fav1()
    fav1()
    fav1()
    

    and

    fav2()
    fav2()
    fav2()
    

    Your country2 doesn't modify the original function and the result is The name of my favorite country is: printed only once during decoration process (at function declaration). While country1 changes the behaviour of the decorated function, you will see the message three times.

    // EDIT: Decorators are just syntactic sugar. The following two snippets are more or less equivalent

    @my_decorator
    def test():
        ...
    

    and

    def test():
        ...
    
    test = my_decorator(test)
    

    In particular we can do weird things with decorators, e.g. they don't have to return functions. For example try this:

    def my_decorator(func):
        return 5
    
    @my_decorator
    def test():
        print("test")
    

    and see what happens. The original function under test symbol is lost, and the test symbol now points to 5.