前言时刻

今天学习了装饰器的用法,很早之前就学习过……

来来总结一波:

今天主要就学习了装饰器的作用以及用法

装饰器很重要,建议弄懂。

1、装饰器概念

装饰器顾名思义就是对一个函数装饰一下扩展一个功能,而不改变其函数内部的代码,去掉装饰器不影响原函数的功能,加上就可扩展一个功能。例如:可以把装饰器比作化妆品,化妆品可以使人脸的变得更加好看,但是擦掉化妆品,脸还是脸。只是使用化妆品装饰了下你的脸变得好看了,丝毫没有改变脸的属性。

有人说装饰器有啥用呢?装饰器完美的符合了代码开放封闭原则。

什么是代码开放封闭原则?

  1. 封闭性:对修改是封闭的,扩展的代码情况下,不能修改原函数的代码
  2. 开放性:允许对已经设计好的代码进行扩展功能。

试想一个场景,今天过节气,老板让你给网站扩展一个充1送一活动功能,你的做法是硬扩、软扩、还是智扩?听我细细道来

  1. def charge_money(pay_sum):
  2. print(f"充值到账:{pay_sum} 元")
  3. return True
  4. # 1、做活动之前
  5. charge_money(666)
  6. # 已经充值了:666 元

1)硬扩:

  1. def charge_money(pay_sum):
  2. print(f"活动赠送了:{pay_sum} 元")
  3. pay_sum = pay_sum * 2
  4. print(f"充值到账:{pay_sum} 元")
  5. return True
  6. # 2、做活动 硬扩
  7. charge_money(666)
  8. # 活动赠送了:666 元
  9. # 充值到账:1332 元

这里有个问题,那就是更改了原函数的内容,不符合封闭性原则。

2)软扩:

  1. # 3、软扩
  2. def salse_activity(pay_sum):
  3. print(f"活动赠送了:{pay_sum} 元")
  4. # 此处省去代码:更新数据库的用户金额
  5. return True
  6. def charge_money(pay_sum):
  7. print(f"充值到账:{pay_sum} 元")
  8. # 此处省去代码:更新数据库的用户金额
  9. return True
  10. salse_activity(666)
  11. charge_money(666)
  12. # 活动赠送了:666 元
  13. # 充值到账:666 元

软扩是在原基础上新添加了一个函数,相当于是扩展了一个功能。但是又有一个问题了,如果你同时在若干 n 个充值函数都添加充一送一活动,那么就需要写 n 次 salse_activity(666) ,那就很不美观。那么有请今天的主角装饰器,闪亮登场。

3)智扩:

  1. # 智扩 装饰器
  2. def salse_activity(func):
  3. """其实就是闭包"""
  4. def inner(pay_sum):
  5. print(f"活动赠送了:{pay_sum} 元")
  6. # 此处省去代码:更新数据库的用户金额
  7. res = func(pay_sum)
  8. return res
  9. return inner
  10. def charge_money(pay_sum):
  11. print(f"充值到账:{pay_sum} 元")
  12. # 此处省去代码:更新数据库的用户金额
  13. return True
  14. charge_money = salse_activity(charge_money)
  15. charge_money(666)
  16. # 活动赠送了:666 元
  17. # 充值到账:666 元

使用 Python 的装饰器语法糖变形有:

  1. # 装饰器 语法糖
  2. def salse_activity(func):
  3. """其实就是闭包"""
  4. def inner(pay_sum):
  5. print(f"活动赠送了:{pay_sum} 元")
  6. # 此处省去代码:更新数据库的用户金额
  7. res = func(pay_sum)
  8. return res
  9. return inner
  10. @salse_activity # 就等于是 charge_money = salse_activity(charge_money)
  11. def charge_money(pay_sum):
  12. print(f"充值到账:{pay_sum} 元")
  13. # 此处省去代码:更新数据库的用户金额
  14. return True
  15. charge_money(666)
  16. # 活动赠送了:666 元
  17. # 充值到账:666 元

装饰器的引入就介绍完了,下面介绍装饰器的具体用法。

2、装饰器参数

装饰器的参数一般会配合*args**kwargs,看起来更加简介明了。

  1. # 初始化
  2. def decorate_A(func):
  3. print("我是装饰器A")
  4. def inner_A(*args):
  5. print("装饰器A被调用")
  6. res = func(*args)
  7. print(f"resA:{res}")
  8. return res + " + 装饰器A 返回值"
  9. return inner_A
  10. def decorate_B(func):
  11. print("我是装饰器B")
  12. def inner_B(*args):
  13. print("装饰器B被调用")
  14. res = func(*args)
  15. print(f"resB:{res}")
  16. return res
  17. return inner_B

给函数添加装饰器并调用传参:

  1. @decorate_A # main_func=decorate_A(main_func) = inner_A
  2. def main_func(*args):
  3. print("我是主函数")
  4. print(args)
  5. return True
  6. main_func(*[1, 2, 3], 4, *(5, 6, 7))
  7. """
  8. 我是装饰器A
  9. 装饰器A被调用
  10. 我是主函数
  11. (1, 2, 3, 4, 5, 6, 7)
  12. resA:True
  13. """

以上涉及到*args,如果不懂得话,可以看我的另一篇博客:args和*args,

3、多层装饰器

多层装饰器是从下往上依次执行,需要注意的是,被装饰的函数名所指代的函数是一直被装饰器中的内层函数所取代。

  1. # 3. main_func = decorate_A(inner_B)(*args)
  2. @decorate_A # 2. main_func=decorate_A(inner_B) = inner_A
  3. @decorate_B # 1. main_func=decorate_B(main_func) = inner_B
  4. def main_func(*args):
  5. print("我是主函数")
  6. print(args)
  7. return "主函数"
  8. res = main_func(*[1, 2, 3], 4, *(5, 6, 7))
  9. print(res)
  10. """
  11. 我是装饰器B
  12. 我是装饰器A
  13. 装饰器A被调用
  14. 装饰器B被调用
  15. 我是主函数
  16. main_func:(1, 2, 3, 4, 5, 6, 7)
  17. resB:True
  18. resA:True
  19. 主函数 + 装饰器A 返回值
  20. """

解释下上面的两个装饰器怎么调用的。

首先:

  1. 调用函数,main_func(*[1, 2, 3], 4, *(5, 6, 7))
  2. 装饰器从下到上执行,先执行@decorate_B,main_func 就变成main_func = decorate_B(main_func) = inner_B打印结果分析:这一步由于调用了decorate_B函数,所以就打印了我是装饰器B
  3. 然后执行装饰器@decorate_A,此时有main_func = decorate_A(main_func),但是在上一步装饰器中main_func已经变成innder_B,所有main_func = decorate_A(inner_B)
    打印结果分析:这一步由于调用了decorate_A函数,所以就打印了我是装饰器A
  4. 最后有main_func = decorate_A(inner_B)(*args)打印结果分析:1)当调用函数 main_func 时,先执行函数 inner_A,单后打印装饰器A被调用。2)然后执行传入的参数 inner_B 函数,此时打印装饰器B被调用。3)执行 inner_B 函数中的函数其实就是被装饰的函数main_func,打印我是主函数,然后打印main_func:(1, 2, 3, 4, 5, 6, 7)
  5. 解释完毕

多层装饰器.png

总结:

装饰器很重要,个人觉得装饰器的设计实在是太巧妙了,但是有点绕不容易理解,建议多看多练多总结。