Python @函数装饰器及用法

Posted on   风行天下-2080  阅读(47)  评论(0编辑  收藏  举报

1、http://c.biancheng.net/view/2270.html

那么,函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示:

  1. #funA 作为装饰器函数
  2. def funA(fn):
  3. #...
  4. fn() # 执行传入的fn参数
  5. #...
  6. return '...'
  7. @funA
  8. def funB():
  9. #...


实际上,上面程序完全等价于下面的程序:

  1. def funA(fn):
  2. #...
  3. fn() # 执行传入的fn参数
  4. #...
  5. return '...'
  6. def funB():
  7. #...
  8. funB = funA(funB)

通过比对以上 2 段程序不难发现,使用函数装饰器 A() 去装饰另一个函数 B(),其底层执行了如下 2 步操作:

  1. 将 B 作为参数传给 A() 函数;
  2. 将 A() 函数执行完成的返回值反馈回  B。


举个实例:

  1. #funA 作为装饰器函数
  2. def funA(fn):
  3. print("C语言中文网")
  4. fn() # 执行传入的fn参数
  5. print("http://c.biancheng.net")
  6. return "装饰器函数的返回值"
  7. @funA
  8. def funB():
  9. print("学习 Python")

程序执行流程为:

C语言中文网
学习 Python
http://c.biancheng.net

在此基础上,如果在程序末尾添加如下语句:

  1. print(funB)

其输出结果为:

装饰器函数的返回值

显然,被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值),即如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名;同样,如果装饰器返回的是一个函数的名称,那么被修饰的函数名依然表示一个函数。

实际上,所谓函数装饰器,就是通过装饰器函数,在不修改原函数的前提下,来对函数的功能进行合理的扩充。

带参数的函数装饰器

在分析 funA() 函数装饰器和 funB() 函数的关系时,细心的读者可能会发现一个问题,即当 funB() 函数无参数时,可以直接将 funB 作为 funA() 的参数传入。但是,如果被修饰的函数本身带有参数,那应该如何传值呢?

比较简单的解决方法就是在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同。例如:

  1. def funA(fn):
  2. # 定义一个嵌套函数
  3. def say(arc):
  4. print("Python教程:",arc)
  5. return say
  6. @funA
  7. def funB(arc):
  8. print("funB():", a)
  9. funB("http://c.biancheng.net/python")

程序执行结果为:

Python教程: http://c.biancheng.net/python


这里有必要给读者分析一下这个程序,其实,它和如下程序是等价的:

  1. def funA(fn):
  2. # 定义一个嵌套函数
  3. def say(arc):
  4. print("Python教程:",arc)
  5. return say
  6. def funB(arc):
  7. print("funB():", a)
  8. funB = funA(funB)
  9. funB("http://c.biancheng.net/python")

如果运行此程序会发现,它的输出结果和上面程序相同。

显然,通过 funB() 函数被装饰器 funA() 修饰,funB 就被赋值为 say。这意味着,虽然我们在程序显式调用的是 funB() 函数,但其实执行的是装饰器嵌套的 say() 函数。

但还有一个问题需要解决,即如果当前程序中,有多个(≥ 2)函数被同一个装饰器函数修饰,这些函数带有的参数个数并不相等,怎么办呢?

最简单的解决方式是用 *args 和 **kwargs 作为装饰器内部嵌套函数的参数,*args 和 **kwargs 表示接受任意数量和类型的参数。举个例子:

  1. def funA(fn):
  2. # 定义一个嵌套函数
  3. def say(*args,**kwargs):
  4. fn(*args,**kwargs)
  5. return say
  6. @funA
  7. def funB(arc):
  8. print("C语言中文网:",arc)
  9. @funA
  10. def other_funB(name,arc):
  11. print(name,arc)
  12. funB("http://c.biancheng.net")
  13. other_funB("Python教程:","http://c.biancheng.net/python")

运行结果为:

C语言中文网: http://c.biancheng.net
Python教程: http://c.biancheng.net/python

函数装饰器可以嵌套

上面示例中,都是使用一个装饰器的情况,但实际上,Python 也支持多个装饰器,比如:

  1. @funA
  2. @funB
  3. @funC
  4. def fun():
  5. #...

上面程序的执行顺序是里到外,所以它等效于下面这行代码:

fun = funA( funB ( funC (fun) ) )

这里不再给出具体实例,有兴趣的读者可自行编写程序进行测试。

 

编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?

随笔 - 618, 文章 - 0, 评论 - 6, 阅读 - 37万

Copyright © 2025 风行天下-2080
Powered by .NET 9.0 on Kubernetes

点击右上角即可分享
微信分享提示