Home >Backend Development >Python Tutorial >Python decorators - closures and function decorators

Python decorators - closures and function decorators

PHPz
PHPzforward
2023-04-10 14:51:071684browse

1. Closure

Before learning decorators, you need to first understand the concept of closure. Key points in forming closures:

  • Function nesting
  • Use the internal function as the return value of the external function
  • The internal function must use the variables of the external function

The following is a case of calculating the average of a list to explain closure:

def make_average():
# 创建一个列表,用来保存数值
nums = []

# 定义一个内部函数,用来计算列表的平均值
def average(n):
# 将数值添加到列表中
nums.append(n)
# 返回平均值
return sum(nums) / len(nums)

return average
  1. First, define a function make_average;
  2. Secondly, in the make_average function Define an empty list internally to store values;
  3. Again, define an internal function to calculate the average of the list;
  4. Finally, use this internal function as the return value of the external function make_average , be careful not to add (), adding () will become calling this function.
# 调用外部函数,并将其复制给一个变量,注意:此时返回的是内函数的内存地址
a = make_average()
# 给这个变量加(),就相当于调用了内函数average
print(a(20))
print(a(30))

The running results are as follows: when the value passed in is 20, there is only one number in the list, so the calculation result is 20; when a value 30 is passed in, there is The two numbers are 20 and 30, so the calculated result of the average is 25.

Python decorators - closures and function decorators

2. Decorator

1. Decorator introduction

For example, there are the following two functions, which calculate the sum of two numbers and the score respectively:

def add(a, b):
"""计算两数之和"""
res = a + b
return res

def mul(a, b):
"""计算两数之积"""
res = a * b
return res

Now there is a requirement: I want to print "Start calculation..." before the calculation of each function starts... ", print "Computation ends..." after the calculation is completed. We can meet this demand by directly modifying the function code, but this will face the following problems:

  1. If there are too many functions to be modified, ten or even a hundred functions, it is unrealistic;
  2. It is not convenient for later maintenance. For example, I don’t want to print "Start calculation...", but I want to print "begin...". Wouldn’t it be necessary to modify it again;
  3. Violation The Open-Closed Principle (OCP), that is, the design of the program, requires opening the extension of the program and closing the modification of the program;

Therefore, the above method of directly modifying the function code is not feasible. We hope to extend the function without modifying the original function. For example:

def new_add(a, b):
print("开始计算...")
r = add(a, b)
print("计算结束...")
return r


print(new_add(22, 33))

The execution result is as follows:

Python decorators - closures and function decorators

Although this method of creating a new function does not modify the original function, it faces a serious problem:

Only the specified function can be extended, and cannot be used universally for other functions. For example, the above-mentioned add function cannot be extended, but the mul function cannot be extended. If you want to extend the mul function, you can only create another extension function;

Because we hope to define a general extension function that can scope all functions. This type of universal function that does not change the original function code is: decorator.

2. Function decorator

A decorator is essentially a python function or class, which allows other functions or classes to add additional functions without making any code modifications. It is to add additional functionality to an existing object. The return value of the decorator is also a function/class object. It is often used in scenarios with cross-cutting requirements, such as: log insertion, performance testing, transaction processing, caching, permission verification, etc.

1) The decorated function does not take parameters

For example:

def wrapper_info(func):
def inner():
print("开始介绍...")
res = func()
print("介绍结束...")
return res

return inner

def introduce1():
print("我是周润发,我来自HONG KONG")

info = wrapper_info(introduce1)
info()

The running result is as follows:

Python decorators - closures and function decorators

is visible, Without changing the original function code, some additional functions are added to the original function. func is the function to be modified. It is passed into the decorated function as a variable and can be used universally for other functions. This wrapper_info is the decorator. But the current problem is, what if the decorated function takes parameters? For example:

def introduce2(name, age):
print(f"我叫{name}, 我今年{age}岁了")

2) The decorated function has parameters

Although name and age can be passed in the decorator wrapper_info, not every decorated function only has name and age. Or a parameter of a specified type, or a dictionary, list, tuple, etc. may be passed in. That is, what should I do if the type and number of parameters passed in are not fixed?

At this time, you need to use variable length parameters: (*args, **kwargs)

def wrapper_info(func):
"""
用来对其他函数进行扩展,使其他函数可以在执行前做一些额外的动作
:param func: 要扩展的函数对象
:return:
"""
def inner(*args, **kwargs):
print("开始介绍...")
res = func(*args, **kwargs)
print("介绍结束...")
return res

return inner

For example:

def introduce3(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")

The running results are as follows:

Python decorators - closures and function decorators

3)装饰器带参数

上述提到的是装饰器,一种是应用于被装饰的函数不带参数,一种是被装饰的函数带参数,那装饰器本身能否带参数呢?比如我定义一个变量,想通过传入不同的值来控制这个装饰器实现不同的功能。答案是肯定的,例如:

def use_log(level):
def decorator(func):
def inner(*args, **kwargs):
if level == "warn":
logging.warning("%s is running by warning" % func.__name__)
elif level == "info":
logging.warning("%s is running by info" % func.__name__)
else:
logging.warning("%s is running by other" % func.__name__)
return func(*args, **kwargs)

return inner

return decorator


def introduce4(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")


info1 = use_log(introduce4('周星驰', 28, '香港'))
info1('info')
info2 = use_log(introduce4('周润发', 28, '香港'))
info2('warn')
info3 = use_log(introduce4('成龙', 28, '香港'))
info3('xxx')

运行结果如下:

Python decorators - closures and function decorators

3.装饰器调用

方式一:以函数方式调用

info3 = wrapper_info(introduce3)
info3('刘德华', 28, '香港')

如果是装饰器函数带参数,则调用方式为:

info4 = use_log(introduce4('周星驰', 28, '香港'))
info4('info')

方式二:以语法糖方式调用

即在被装饰函数上方以@符号进行修饰

@wrapper_info
def introduce3(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")

introduce3('刘德华', 28, '香港')

如果是装饰器函数带参数,例如上述的use_log,则需要在装饰器中传入参数:

@use_log('info')
def introduce4(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")

小结

什么是装饰器?

在不改变原函数代码的情况下,给原函数增加了一些额外的功能,并且能够通用于其他函数,这样的函数就称作为装饰器。

装饰器的调用

可以通过传统调用函数的方式进行调用,也可以通过@装饰器的方式调用

装饰器的特点

  • 通过装饰器,可以在不修改原来函数的情况下对函数进行扩展
  • 一个函数可以同时指定多个装饰器

The above is the detailed content of Python decorators - closures and function decorators. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:51cto.com. If there is any infringement, please contact admin@php.cn delete