Python-closures & decorators

Closure:

The format of the closure:

Look at the following piece of code

def fun(a):
    b = 10

    def inner_fun():
        print(a + b)
    return inner_fun


x = fun(5)
print(type(x))
print(x)
x()
'''
输出:
<class 'function'>
<function fun.<locals>.inner_fun at 0x000001BB0CB02948>
15
'''

It can be found that an internal function is defined in a function, and this internal function is returned as a value by the external function.
You can see that using type to print x, the result is that <class'function'> proves that it returns a function, and the next paragraph of printing indicates that this function is an internal function of the fun function. You can also see a piece of memory address.
That is, after the above operation, the variable x gets the memory address of the internal function inner_fun. Calling x is equivalent to calling inner_fun. The function and use in this case are called closures.

Characteristics of closures
  1. First of all, regarding internal functions, it is possible to access variables of external functions. But when modifying external function variables, only variable types of variables can be modified by default. If you want to modify a variable of an immutable type, you must declare it with nonlocal.
def fun(a):
    b = 10

    def inner_fun():
    nonlocal b # 这里需要声明
        b += a 
    return inner_fun


x = fun(5)
  1. Closures can save the state of the function, that is, the function will not be recycled immediately after execution, and it can still be used. In the following example, you can see that after the function is executed, you can still operate on the cnt variable
def fun(ini):
    cnt = ini
    print(ini)

    def inner_fun(a):
        nonlocal cnt
        cnt += a
        print(cnt)
    return inner_fun


F = fun(5)
F(10)
F(1)
'''
输出:
5
15
16
'''
  1. The functions obtained outside the closure do not interfere with each other. You can see below that even if the parameters are the same, the function addresses they get are not the same, indicating that they will not interfere with each other when operating F1 and F2 later.
def fun(ini):
    cnt = ini
    print(ini)

    def inner_fun(a):
        nonlocal cnt
        cnt += a
        print(cnt)
    return inner_fun


F1 = fun(5)
F2 = fun(5)
print(F1)
print(F2)
'''
输出:
5
5
<function fun.<locals>.inner_fun at 0x000002209D792948>
<function fun.<locals>.inner_fun at 0x000002209D767678>
'''

Decorator

Decorator format

Looking at the following example, you can find that I passed a function as a parameter to fun. From the output, you can see that fun1 is called and inner_fun is called. It can be concluded that when a function is used as a parameter, it can still be called by an internal function.

def fun(f):
    def inner_fun():
        f()
        print('内部函数的内容')
    return inner_fun


def fun1():
    print('fun1的内容')


x = fun(fun1)
x()
'''
输出:
fun1的内容
内部函数的内容
'''

Looking at the following code again, you can find that even if the internal function is not returned by closure, inner_fun is executed when fun1 is called. At this point, you can find that there is an extra piece of @fun in
the code. The function of this code is to add a decorator to the following function, namely fun1, and the above fun function is a decorator

def fun(f):
    def inner_fun():
        f()
        print('内部函数的内容')
    return inner_fun


@fun
def fun1():
    print('fun1的内容')


fun1()
'''
输出:
fun1的内容
内部函数的内容
'''
Decorating process

First look at the following code, you can find that even if nothing is called, the fun function is still used, and you can know that inner_fun is not used

def fun(f):
    print('装饰器开始执行')

    def inner_fun():
        f()
        print('内部函数的内容')
    print('装饰器执行完毕')
    return inner_fun


@fun
def fun1():
    print('fun1的内容')
'''
输出:
装饰器开始执行
装饰器执行完毕
'''

Then look at the following code, you can find that the function returned when printing fun1 turned out to be inner_fun. So you can make a bold guess. When using @fun, py does a default processing operation, which is to use fun1 = fun(fun1) to modify the original fun1.

def fun(f):
    print('装饰器开始执行')

    def inner_fun():
        f()
        print('内部函数的内容')
    print('装饰器执行完毕')
    return inner_fun


@fun
def fun1():
    print('fun1的内容')


print(fun1)
'''
输出:
装饰器开始执行
装饰器执行完毕
<function fun.<locals>.inner_fun at 0x00000128B7F0E0D8>
'''

Therefore, it can be concluded that when the decorator is added, the following function will be passed into the decorator function as a parameter by default at runtime, and the return value of the decorator will replace the original function

Decorate functions with parameters

What happens when the decorated function takes parameters? Take a look at the code below.
It can be found that an error will be reported, why?
Because the fun1 at this time is not the original fun1, it is now inner_fun, and inner_fun has no formal parameters, so an error has occurred, and it is easy to correct it. Just add inner_fun with a parameter.

def fun(f):

    def inner_fun():
        f()
        print('内部函数的内容')
    return inner_fun


@fun
def fun1(x):
    print('fun1的内容----{}'.format(x))


fun1(5)

Fix ~

def fun(f):

    def inner_fun(x):
        f(x)# 这里也要带参数,因为这里的调用的是原本的fun1
        print('内部函数的内容')
    return inner_fun


@fun
def fun1(x):
    print('fun1的内容----{}'.format(x))


fun1(5)

When there are multiple parameters, even keyword parameters, you can use variable parameters just like a normal function

def fun(f):

    def inner_fun(*args, **kwargs):
        f(*args, **kwargs)# 这里需要拆包,因为是要当实际参数使用的
        print('内部函数的内容')
    return inner_fun


@fun
def fun1(x):
    print('fun1的内容----{}'.format(x))


@fun
def fun2(x, y):
    print('fun2的内容----{}和{}'.format(x, y))


fun1(5)
fun2(8, 9)
'''
输出:
fun1的内容----5
内部函数的内容
fun2的内容----8和9
内部函数的内容
'''
Multilayer decorator

Look at the code below. What if you use two decorators for a function?
From the output results, it is clear that when using multi-layer decorators, python will first load the decorator closest to the function.
That is, first will run decorate2 and then decorate1, you can output and view, the last function is the internal function of decorate1

def decorate1(f):
    print('装饰1载入')

    def inner_decorate(*args, **kwargs):
        f(*args, **kwargs)
        print('执行装饰1')
    return inner_decorate


def decorate2(f):
    print('装饰2载入')

    def inner_decorate(*args, **kwargs):
        f(*args, **kwargs)
        print('执行装饰2')
    return inner_decorate


@decorate1
@decorate2
def fun1(x):
    print('fun1的内容----{}'.format(x))


fun1(5)
'''
输出:
装饰2载入
装饰1载入
fun1的内容----5
执行装饰2
执行装饰1
'''
Decorator with parameters

Sometimes, the decorator may need to take a parameter, so how to write a decorator with parameters?
Take a look at the following wording:
This is a wrong wording, because the following function is to be passed in the decorator by default, and other parameters cannot be passed. But what if you want to adjust the decorator according to some parameters when decorating the function?

def decorate1(f):
    print('装饰1载入')

    def inner_decorate(*args, **kwargs):
        f(*args, **kwargs)
        print('执行装饰1')
    return inner_decorate


@decorate1(5)
def fun1(x):
    print('fun1的内容----{}'.format(x))


print(fun1)

Then, Matryoshka Dafa is infinitely good.
You only need to set another layer of external function and return the original function as the return value. Think of it this way. When you execute @, you need to get a function, and decorate(5) returns a function, then It is equivalent to @ is an internal function, that is, @ is dec

def decorate(a: int):
    def dec(f):
        print('装饰1载入:{}'.format(5))

        def inner_decorate(*args, **kwargs):
            f(*args, **kwargs)
            print('执行装饰1')
        return inner_decorate
    return dec


@decorate(5)
def fun1(x):
    print('fun1的内容----{}'.format(x))


fun1(100)

Guess you like

Origin blog.csdn.net/qq_36102055/article/details/107115948