第六章 装饰器
概述:装饰器是一种在不修改原函数代码的前提下,对函数进行增强的工具。 它是 Python 中非常强大的语法特性,常用于:日志、校验、计时、缓存、权限控制等。类似于Java编程体系中Spring框架中的AOP(面向切面编程),也是一种在程序中进行逻辑增强的一种有效方式。
核心语法:· 装饰器是一种可调用对象(通常是函数),接收一个函数作为参数,并返回一个新函数。
1. 函数装饰器
需求:在不修改add函数的前提下,给add函数增加一些额外的功能,例如:计算前打印一句欢迎语。
# 关键点:
# 1.接收被装饰的函数、同时返回新函数(wrapper)
# 2.装饰器“吐出来”的是 wrapper 函数,以后别人调用的也是 wrapper 函数。
# 3.为了保证参数的兼容性,wrapper 函数要通过 *args 和 **kwargs 接收参数。
# 4.wrapper 函数中主要做的是:调用原函数(被装饰的函数)、执行其它逻辑,但要记得将原函数的返回值 return 出去。
defsay_hello(func):
defwrapper(*args, **kwargs):
print("hello Python, 我是计算的打印日志")
return func(*args, **kwargs)
return wrapper
定义装饰器核心规则:
- 接收被装饰的函数,同时返回新函数(wrapper)。
- 装饰器
吐出来的是wrapper函数,以后别人调用的也是wrapper函数。 - 为了保证参数的兼容性,wrapper函数要通过
*args和**kwargs接收参数。 - wrapper函数中主要做的是:调用原函数(被装饰的函数),执行其它逻辑,但要记得将原函数的返回值return出去。
# 关键点:
# 1.接收被装饰的函数、同时返回新函数(wrapper)
# 2.装饰器“吐出来”的是 wrapper 函数,以后别人调用的也是 wrapper 函数。
# 3.为了保证参数的兼容性,wrapper 函数要通过 *args 和 **kwargs 接收参数。
# 4.wrapper 函数中主要做的是:调用原函数(被装饰的函数)、执行其它逻辑,但要记得将原函数的返回值 return 出去。
defsay_hello(func):
defwrapper(*args, **kwargs):
print("hello Python, 我是计算的打印日志")
return func(*args, **kwargs)
return wrapper
defadd(x, y, z):
res = x + y + z
print(f'{x}和{y}和{z}相加的结果是:{res}')
return res
# 正常调用add函数,不存在装饰器
# result = add(1, 2, 3)
# print(result) # 6
# 调用say_hello装饰器,对add函数进行装饰,并得到装饰后的新函数
# 需求:在不修改add函数的前提下,给add函数增加一些额外的功能,例如:计算前先打印一句欢迎语。
# 实现方案:使用装饰器
# 下面这行代码,是手动装饰,写起来会麻烦一些,不推荐,推荐:@装饰器名
add = say_hello(add)
result = add(1, 2, 3)
print(result)
# 执行结果如下:
# hello Python, 我是计算的打印日志
# 1和2和3相加的结果是:6
# 6
上述代码的执行流程:
@say_hello 会自动执行: add = say_hello(add)。
以后调用 add()时,真正执行的是wrapper()装饰器。
语法糖 @在Python中称为装饰器语法,是一种特殊的语法糖。它用于修改或增强函数/类的功能,而无需直接修改其源代码。
defsay_hello(func):
defwrapper(*args, **kwargs):
print(' hello Python, 我是计算的打印日志')
return func(*args, **kwargs)
return wrapper
@say_hello
defadd(x, y, z):
res = x + y + z
print(f'{x}和{y}和{z}相加的结果是:{res}')
return res
result1 = add(1, 2, 0)
print(result1)
# 执行结果如下:
# hello Python, 我是计算的打印日志
# 1和2和3相加的结果是:6
# 6
需求:装饰不同的函数,打印不同的欢迎语。
核心:带参数的装饰器最终是三层嵌套结构:外层接收配置、中间层接收函数、内层接收具体参数。
# 进阶:带参数的装饰器(三层嵌套,外层接收配置,中间层接收函数,内存接收具体参数)
defsay_hello(msg):
defouter(func):
defwrapper(*args, **kwargs):
print(f'Hello,我要开始{msg}计算了')
return func(*args, **kwargs)
return wrapper
return outer
# 装饰加法函数
@say_hello('加法')
defadd(x, y, z):
res = x + y + z
print(f'[x]和[y]和[z]相加的结果是:{res}')
return res
# 装饰减法函数
@say_hello('减法')
defsub(x, y):
res = x - y
print(f'[x]和[y]相减的结果是:{res}')
return res
result1 = add(1, 2, 3)
print(result1)
# 执行结果如下:
# Hello,我要开始加法计算了
# [x]和[y]和[z]相加的结果是:6
# 6
result2 = sub(3, 2)
print(result2)
# 执行结果如下:
# Hello,我要开始减法计算了
# [x]和[y]相减的结果是:1
# 1
核心:注意装饰顺序,距离函数最近的装饰器,会先工作。
例如下面代码:test2先装饰,test1再装饰。
# 进阶:多个装饰器一起使用
deftest1(func):
print('我是test1装饰器')
defwrapper(*args, **kwargs):
result = func(*args, **kwargs)
print('test1追加的逻辑')
return result
return wrapper
deftest2(func):
print('我是test2装饰器')
defwrapper(*args, **kwargs):
result = func(*args, **kwargs)
print('test2追加的逻辑')
return result
return wrapper
@test1
@test2
defadd(x, y):
res = x + y
print(f'[x]和[y]相加的结果是{res}')
return res
result = add(1, 2)
print(result)
# 执行结果如下:
# 我是test2装饰器
# 我是test1装饰器
# [x]和[y]相加的结果是3
# test2追加的逻辑
# test1追加的逻辑
# 3
2. 类装饰器
像调用函数一样,去调用类装饰器的实例对象,就会触发__call__方法的调用。
__call__方法通常接收一个函数作为参数,并且会返回一个新函数。
需求和之前一样,还是给add函数增加"打印欢迎语"的功能。
classSayHello:
def__call__(self, func):
defwrapper(*args, **kwargs):
print("hello Python,我要开始计算了")
return func(*args, **kwargs)
return wrapper
defadd(x, y):
res = x + y
print(f'{x}和{y}相加的结果是{res}')
return res
# 正常调用add函数
result = add(1, 2)
print(result)
# 执行结果如下:
# 1和2相加的结果是3
# 3
使用类装饰器的流程:先创建类的实例对象,随后调用实例对象,并传入要装饰的函数。
# 类装饰器:
# 1.包含 __call__ 方法的类,就是类装饰器。
# 2.像调用函数一样,去调用类装饰器的实例对象,就会触发 __call__ 方法的调用。
# 3.__call__ 方法通常接收一个函数作为参数,并且会返回一个新函数。
classSayHello:
def__call__(self, func):
defwrapper(*args, **kwargs):
print("hello Python,我要开始计算了")
return func(*args, **kwargs)
return wrapper
defadd(x, y):
res = x + y
print(f'{x}和{y}相加的结果是{res}')
return res
# 使用SayHello去装饰add函数(手动调用)
say = SayHello()
add = say(add)
result = add(1, 2)
print(result)
# 执行结果如下:
# hello Python,我要开始计算了
# 1和2相加的结果是3
# 3
通过语法糖@使用装饰器时,类名后要加圆括号调用。
# 类装饰器:
# 1.包含 __call__ 方法的类,就是类装饰器。
# 2.像调用函数一样,去调用类装饰器的实例对象,就会触发 __call__ 方法的调用。
# 3.__call__ 方法通常接收一个函数作为参数,并且会返回一个新函数。
classSayHello:
def__call__(self, func):
defwrapper(*args, **kwargs):
print("hello Python,我要开始计算了")
return func(*args, **kwargs)
return wrapper
# 使用@语法糖使用装饰器
@SayHello()
defadd(x, y):
res = x + y
print(f'{x}和{y}相加的结果是{res}')
return res
# 正常调用add函数
result = add(1, 2)
print(result)
# 执行结果如下:
# hello Python,我要开始计算了
# 1和2相加的结果是3
# 3
带参数的类装饰器写起来,要比带参数的函数装饰器简单,不需要三层嵌套结构。
# 带参数的类装饰器
classSayHello:
def__init__(self, msg):
self.msg = msg
def__call__(self, func):
defwrapper(*args, **kwargs):
print(f'Hello,我要开始{self.msg}计算了')
return func(*args, **kwargs)
return wrapper
@SayHello('加法')
defadd(x, y):
res = x + y
print(f'{x}和{y}相加的结果是{res}')
return res
result = add(1, 2)
print(result)
# 执行结果如下:
# Hello,我要开始加法计算了
# 1和2相加的结果是3
# 3
和之前的函数装饰器一样,离函数近的装饰器,先工作。
# 多个类装饰器的使用
classTest1:
def__call__(self, func):
defwrapper(*args, **kwargs):
print('我是Test1追加的逻辑')
return func(*args, **kwargs)
return wrapper
classTest2:
def__call__(self, func):
defwrapper(*args, **kwargs):
print('我是Test2追加的逻辑')
return func(*args, **kwargs)
return wrapper
@Test1()
@Test2()
defadd(x, y):
return x + y
print(f'{x}和{y}相加的结果是{res}')
return res
result = add(1, 2)
print(result)
# 执行结果如下:
# 我是Test1追加的逻辑
# 我是Test2追加的逻辑
# 3