This is the 7th day of my participation in the More Text Challenge.

  1. Simple decorator
  2. Decorator pass parameter
  3. Whether the decorator is compatible with passing parameters
  4. Execution order of multiple decorators

Before we get to decorators, let’s talk about closures.

closure

There is the following function:

def outer(a, b) :
    print(a, b)
    def inner() :
        print(a + 1, b + 1)
    return inner

outer_result = outer(Awesome!.888)
print(outer_result)
print(type(outer_result))
outer_result()
Copy the code

The output

Awesome! 888
<function outer.<locals>.inner at 0x100fcc158>
<class 'function'> 667, 889Copy the code

Closures implement one very important feature: parameter caching. I can put all the arguments aside, do something else, and then call the inner function when I’m ready

The simplest decorator

A decorator is an implementation of a closure, and a decorator is a closure. However, closures typically pass in variables, while decorators pass in function names (the physical location of the function definition in memory, similar to Pointers). Let’s say we need to cook a dish,

def cooking() :
    print("Wash dish")
    print("Wide oil in the pan.")
    print("The dish falls into the pot.")
    print("Add seasoning.")
    print("Patient stir-fry.")
    print("Taste it.")
    print("Install disc")
Copy the code

Now cooking function has, but there is no ingredients ah, clever housewife can not cook without rice, and then we do not want to write in the cooking function has nothing to do with cooking, so the decoration can be implemented in the basis of not changing the cooking function to buy food first.

def buy_online(func) :
    def wrapper() :
        print("Online fresh home buy buy buy buy")
        func()
    return wrapper


@buy_online
def cooking() :
    print("Wash dish")
    print("Wide oil in the pan.")
    print("The dish falls into the pot.")
    print("Add seasoning.")
    print("Patient stir-fry.")
    print("Taste it.")
    print("Install disc")


cooking()
Copy the code

The output

Online fresh go home buy buy buy wash vegetables wide oil next to the pot next to the dish off the pot add seasoning patiently stir fry taste taste plateCopy the code

The buy_online above is a closure, except that the argument now passed in is a function. So if the arguments that are passed to the closure are functions, that function is called a decorator. Using decorators is as simple as appending the @ decorator function name above the function to be decorated.

How the @ works

@ passes the following decorated function name as an argument to the decorator, and then assigns the return value of the decorator to the decorated function name. The decorated function name holds the address of the inner function of the decorator.

Decorator pass parameter

In order to realize the different processing of decorators in different situations, it is necessary to control the parameter passing to decorators. To cache decorator parameters, you need another layer of closures, that is, two layers of closures to pass parameters to the decorator.

def buy_online(mall) :
    def which_mall_online(func) :

        def wrapper(*args, **kwargs) :
            print("'{}' online fresh home buy buy buy".format(mall))
            func(*args, **kwargs)

        return wrapper

    return which_mall_online


@buy_online('Fresh of the Day')
def cooking(*args, **kwargs) :
    print("Wash dish")
    print("Wide oil in the pan.")
    print("The dish falls into the pot.")
    print("Add seasoning.")
    print("Patient stir-fry.")
    print("Taste it.")
    print("Install disc")
Copy the code

The output

'Fresh of the Day'Online fresh go home buy buy buy wash vegetables wide oil next to the pot next to the dish off the pot add seasoning patiently stir fry taste taste plateCopy the code

Decorators are compatible with passing and no passing parameters

If you want the buy_online decorator above to support both passable and no-passable arguments or to call as in the first example, how do you implement a decorator that also supports the following call compatibility?

@buy_online
@buy_online('Fresh of the Day')
@buy_online()
Copy the code

Compatible solution idea:

  1. The difference between passing and not passing parameters is that passing parameters need to be wrapped again in the closure
  2. If there is no argument, calling the outermost function manually returns the second inner function
def buy_online(decorated_func=None, mall=None) :
    if decorated_func and mall:
        raise Exception('Decorator decorated functions and decorator arguments do not appear in the same place')

    def which_mall_online(func) :

        def wrapper(*args, **kwargs) :
            nonlocal mall
            if mall == None:
                mall = ' '
            print("{} online fresh go home buy buy buy".format(mall))
            func(*args, **kwargs)

        return wrapper
    if decorated_func:
        If a decorated function is passed, return the innermost wrapper function
        return which_mall_online(decorated_func)
    else:
        If the decorator has arguments or uses default arguments, return the outer function
        return which_mall_online


@buy_online(mall=Are you hungry? Fresh?)
def cooking(*args, **kwargs) :
    print("Wash dish")
    print("Wide oil in the pan.")
    print("The dish falls into the pot.")
    print("Add seasoning.")
    print("Patient stir-fry.")
    print("Taste it.")
    print("Install disc")


@buy_online()
def cooking_default_parameter(*args, **kwargs) :
    print("Wash dish")
    print("Wide oil in the pan.")
    print("The dish falls into the pot.")
    print("Add seasoning.")
    print("Patient stir-fry.")
    print("Taste it.")
    print("Install disc")


@buy_online
def cooking_decorator_no_parameter(*args, **kwargs) :
    print("Wash dish")
    print("Wide oil in the pan.")
    print("The dish falls into the pot.")
    print("Add seasoning.")
    print("Patient stir-fry.")
    print("Taste it.")
    print("Install disc")

# Execute properly
cooking()
cooking_default_parameter()
cooking_decorator_no_parameter()
Copy the code

Execution order of multiple decorators

To illustrate the order of execution of multiple decorators, we take the last to reach the peak of life as an example. There are many ups and downs or different stages to go through before you reach the peak of your life. So we reach our peak? “For the decorated function, keep superimposing different steps on top of it.

Zygote baby Kindergarten primary school Junior high School University Society University to the peak of life?Copy the code

Stage decorators

def zero_stage(func) :
    print("Enter zero_stage function definition")

    def zero_inner() :
        print("It's still a fertilized egg.")
        func()

    return zero_inner


def kindergarten_stage(func) :
    print(Enter the kindergarten_stage function definition)

    def kindergarten_inner() :
        print("I went to kindergarten with my head in my head.")
        func()

    return kindergarten_inner


def primary_school_stage(func) :
    print("Enter primary_school_stage function definition")

    def primary_school_stage_inner() :
        print("Schoolchildren who wear red scarves every day.")
        func()

    return primary_school_stage_inner


def middle_school_stage(func) :
    print("Enter middle_school_stage function definition")

    def middle_school_stage_inner() :
        print("Junior high school students taking evening classes.")
        func()

    return middle_school_stage_inner


def high_school_stage(func) :
    print("Enter the high_school_stage function definition")

    def high_school_stage_inner() :
        print("A high school student who can only go home once a month.")
        func()

    return high_school_stage_inner


def university_stage(func) :
    print(Enter the university_stage function definition)

    def university_inner() :
        print("The college student who learned binary CS.")
        func()

    return university_inner


def social_stage(func) :
    print("Enter social_stage function definition")

    def social_inner() :
        print("Social university, unfathomable.")
        func()

    return social_inner
Copy the code

To the top of your life

def peak_of_life() :
    print("To the top of my game.")
Copy the code

The order in which functions are defined

@zero_stage @kindergarten_stage @primary_school_stage @middle_school_stage @high_school_stage @university_stage @social_stage def peak_of_life(): print(" Going to the top of your life?" )Copy the code

The output

Enter social_stage, enter university_stage, enter high_school_stage, enter middle_school_stage, enter primary_school_stage Kindergarten_stage function definition Kindergarten_stage function definitionCopy the code

The order in which functions are defined

In the end,

Peak_of_life points to zero_inner,

Zero_inner’s func refers to Kindergarten_inner,

Kindergarten_inner’s func points to primary_school_stage_inner,

And so on,….

The order in which a function is called

peak_of_life()
Copy the code

The output

Is it a fertilized egg, muddleheaded, entered the kindergarten, a primary school student wearing a red scarf every day, attended evening self-study, a junior high school student who can only go home once a month, a high school student who has known binary CS, a college student, a social university, and an unfathable life peak?Copy the code

The order in which functions are called, because the order in which functions are defined determines the order in which they are called. Peak_of_life () is zero_inner(), and so on

The order in which multiple decorators are summarized

When the decorator function is defined, the decorator function is called from the bottom up, and the decorator function is executed from the top down:

  1. Functions that are decorated by decorators are defined like clothes, wearing the innermost first
  2. The function decorated by the decorator is called as if it were undressed, stripping the outermost first