鱼C论坛

 找回密码
 立即注册
查看: 10809|回复: 37

[知识点备忘] 第046讲:函数(VI)- 装饰器

[复制链接]
发表于 2022-1-7 05:12:09 | 显示全部楼层 |阅读模式
购买主题 已有 25 人购买  本主题需向作者支付 5 鱼币 才能浏览
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2022-4-8 17:26:56 | 显示全部楼层
本节首先回顾了闭包的原理,而后通过两个函数作为参数传递给另一个函数来调用的例子引入了装饰器的概念——所谓装饰器,就是在不修改原来代码的前提下增加新功能,拿函数作为参数,调用装饰器函数,实则还是利用了闭包的特性。“@+装饰器”其实是一个语法糖,等价于“函数 = 装饰器(函数)”;前者写法简单,使用起来更方便!此外,多个装饰器可以用在同一个函数上,还可以通过函数的多层嵌套调用给装饰器传递参数。尽管看上去很复杂,背后的原理很简单。作为一名程序员,只要掌握了装饰器的精髓,就再也不用担心产品经理给我们出难题啦!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

发表于 2022-5-2 19:01:40 | 显示全部楼层
装饰器可以实现在不修改原来代码的前提下增加新功能。

(通过将原来代码作为函数,传入装饰器,返回新的结果)

  1. import time

  2. def time_master(func):
  3.     def call_func():
  4.         print("开始运行程序...")
  5.         start = time.time()
  6.         func()
  7.         stop  = time.time()
  8.         print("结束程序运行...")
  9.         print(f"一共耗费了 {(stop-start):.2f} 秒.")
  10.     return call_func

  11. @time_master
  12. def myfunc():
  13.     time.sleep(2)
  14.     print("Hello Python.")

  15. myfunc()
  16. >>>
  17. 开始运行程序...
  18. Hello Python.
  19. 结束程序运行...
  20. 一共耗费了 2.13 秒.
复制代码


此外,多个装饰器可以用在同一个函数上,还可以通过函数的多层嵌套调用给装饰器传递参数。

(多个@装饰器时,执行顺序是从下往上)

  1. def square(func):
  2.     def inner():
  3.         x = func()
  4.         return x ** 2
  5.     return inner

  6. def add(func):
  7.     def inner():
  8.         x = func()
  9.         return x + 3
  10.     return inner

  11. @square
  12. @add
  13. def test():
  14.     return 2

  15. print(test()) # 顺序为(2+3)** 2
  16. >>>
  17. 25
复制代码


装饰器也可以传入参数。

  1. import time

  2. def logger(msg):
  3.     def time_master(func):
  4.         def inner():
  5.             start = time.time()
  6.             func()
  7.             stop  = time.time()
  8.             print(f"[{msg}]一共耗费了 {(stop-start):.2f} 秒.")
  9.         return inner
  10.     return time_master

  11. @logger(msg='A') # 等同于funA = logger(msg=‘A’)(funA)
  12. def funA():
  13.     print("正在运行程序funA...")
  14.     time.sleep(1)

  15. @logger(msg='B')
  16. def funB():
  17.     print("正在运行程序funB...")
  18.     time.sleep(2)
  19.    
  20. funA()
  21. funB()
  22. >>>
  23. 正在运行程序funA...
  24. [A]一共耗费了 1.08 秒.
  25. 正在运行程序funB...
  26. [B]一共耗费了 2.03 秒.
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-1 23:41:52 | 显示全部楼层
  1. import time
  2.    
  3. def logger(msg):
  4.     def time_master(func):
  5.         def useless():
  6.             
  7.             def call_func():
  8.                 start = time.time()
  9.                 func()
  10.                 stop = time.time()
  11.                 print(f"[{msg}]一共耗费了 {(stop-start):.2f}")
  12.             return call_func
  13.         return useless()
  14.     return time_master
  15.    
  16. @logger(msg="A")
  17. def funA():
  18.     time.sleep(1)
  19.     print("正在调用funA...")
  20.    
  21. @logger(msg="B")
  22. def funB():
  23.     time.sleep(1)
  24.     print("正在调用funB...")
  25.    
  26. funA()
  27. funB()
复制代码

第三个知识点让我好奇,这个语法糖是否可以不断向内挖掘嵌套函数,直到挖到最底层函数,然后将这个最底层的函数赋值给原函数?
这里在在logger函数中间添加了一个useless用来检测,经过实践确实是这样的,我随后还试了试两个useless,也是同样的结果
  1. 正在调用funA...
  2. [A]一共耗费了 1.01
  3. 正在调用funB...
  4. [B]一共耗费了 1.00
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

发表于 2022-9-1 15:45:42 | 显示全部楼层
装饰器第一部分的闭包过程中不打 return call_func  为什么报错
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-9-8 10:36:47 | 显示全部楼层
需要注意的是   ‘return 函数名’   的后面是否加‘()’
  1. def cube(func):
  2.     def inner():
  3.         def a():
  4.             def b():
  5.                 x = func()
  6.                 return x * x * x
  7.             return b()
  8.         return a()
  9.     return inner
  10.    
  11. @cube
  12. def test():
  13.     return 2
  14.    
  15. print(test())
复制代码

等价于
  1. def cube(func):
  2.     def inner():
  3.         def a():
  4.             def b():
  5.                 x = func()
  6.                 return x * x * x
  7.             return b
  8.         return a
  9.     return inner
  10.    
  11. @cube
  12. def test():
  13.     return 2
  14.    
  15. print(test()()())
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-9-25 12:18:31 | 显示全部楼层

我觉得可以这么理解语法糖
import time

def time_master(func):
    def call_func():
        print('开始运行程序...')
        start = time.time()
        func()                     # 函数嵌套调用   func是形参,myfunc是实参  因此这里会执行myfunc()函数
        stop = time.time()
        print('结束程序运行...')
        print(f'一共耗费了{(stop-start):.2f}秒')
    return call_func            # 嵌套函数的返回值 返回到>>>函数调用处

def myfunc():
    time.sleep(2)
    print('Python')
a = time_master(myfunc)   #这是函数调用处  》》也就是说 a = time_master(myfunc)就是call_func
a()                                   # 所以这里就是 call_func()
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-9-25 12:48:24 | 显示全部楼层
8245k 发表于 2022-8-1 23:41
第三个知识点让我好奇,这个语法糖是否可以不断向内挖掘嵌套函数,直到挖到最底层函数,然后将这个最底层 ...

我认为是由于func()这个嵌套调用在最最内存函数call_func()中,所以最后实质上才是call_func()这个函数的实现
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-9-28 21:21:55 | 显示全部楼层
本帖最后由 阿伟同学 于 2022-9-28 21:23 编辑
8245k 发表于 2022-8-1 23:41
第三个知识点让我好奇,这个语法糖是否可以不断向内挖掘嵌套函数,直到挖到最底层函数,然后将这个最底层 ...


问题不在这,是您retrun useless() 加上括号了按小甲鱼等价的
  1. funA = logger(msg = "A")(funA)
复制代码
你这个相当于还是返回call_func的引用,你要是把括号去了就大不一样了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-6 17:10:01 | 显示全部楼层
难成狗啊,我要多看几次才行,坚持把这吃透
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-6 17:37:56 | 显示全部楼层
装饰器,确实很强大, 但是理解起来确实比较费力,继续加油
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-14 15:23:46 | 显示全部楼层
滴滴滴~打卡
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-16 16:47:58 | 显示全部楼层
测试函数不能带参数,不然会报错,这是为什么呢?比如只能funA(),而不能是funA(x)
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-21 14:31:01 | 显示全部楼层
8245k 发表于 2022-8-1 23:41
第三个知识点让我好奇,这个语法糖是否可以不断向内挖掘嵌套函数,直到挖到最底层函数,然后将这个最底层 ...

不会把,装饰器只会将函数作为参数传一次,你一直往下嵌套直到最底层,是因为你返回函数的时候加了()
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-25 10:37:03 | 显示全部楼层
语法糖  很赞啊
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-10-25 10:41:25 | 显示全部楼层
装饰器相对于语法糖,但是看起来更简洁直接
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-7 18:43:35 | 显示全部楼层
Learning...
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-11-25 20:51:50 | 显示全部楼层
七钱五 发表于 2022-10-16 16:47
测试函数不能带参数,不然会报错,这是为什么呢?比如只能funA(),而不能是funA(x)

我也遇到了同样的问题,想知道后面怎么解决了?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-12-30 22:32:16 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2023-1-2 13:52:09 | 显示全部楼层
打卡
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-23 00:27

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表