前面介绍的 @staticmethod 和 @classmethod 的本质就是函数装饰器,其中 staticmethod 和 classmethod 都是 Python 内置的函数。

使用 @ 符号引用已有的函数(比如 @staticmethod、@classmethod)后,可用于修饰其他函数,装饰被修饰的函数。那么我们是否可以开发自定义的函数装饰器呢?

答案是肯定的。当程序使用“@函数”(比如函数 A)装饰另一个函数(比如函数 B)时,实际上完成如下两步:

将被修饰的函数(函数 B)作为参数传给 @ 符号引用的函数(函数 A)。

将函数 B 替换(装饰)成第 1 步的返回值。

从上面介绍不难看出,被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西。

为了让大家厘清函数装饰器的作用,下面看一个非常简单的示例:

def funA(fn): print('A') fn() # 执行传入的fn参数 return 'fkit' ''' 下面装饰效果相当于:funA(funB), funB将会替换(装饰)成该语句的返回值; 由于funA()函数返回fkit,因此funB就是fkit ''' @funA def funB(): print('B') print(funB) # fkit

上面程序使用 @funA 修饰 funB,这意味着程序要完成两步操作:

将 funB 作为 funA() 的参数,也就是上面代码中 @funA 相当于执行 funA(funB)。

将 funB 替换成上一步执行的结果,funA() 执行完成后返回 fkit,因此 funB 就不再是函数,而是被替换成一个字符串。

运行上面程序,可以看到如下输出结果:

A

B

Fkit

通过这个例子,相信读者对函数装饰器的执行关系己经有了一个较为清晰的认识,但读者可能会产生另一个疑问:这个函数装饰器导致被修饰的函数变成了字符串,那么函数装饰器有什么用?

别忘记了,被修饰的函数总是被替换成 @ 符号所引用的函数的返回值,因此被修饰的函数会变成什么,完全由于 @ 符号所引用的函数的返回值决定,换句话说,如果 @ 符号所引用的函数的返回值是函数,那么被修饰的函数在替换之后还是函数。

下面程序示范了更复杂的函数装饰器:

def foo(fn): # 定义一个嵌套函数 def bar(*args): print("===1===", args) n = args[0] print("===2===", n * (n - 1)) # 查看传给foo函数的fn函数 print(fn.__name__) fn(n * (n - 1)) print("*" * 15) return fn(n * (n - 1)) return bar ''' 下面装饰效果相当于:foo(my_test), my_test将会替换(装饰)成该语句的返回值; 由于foo()函数返回bar函数,因此funB就是bar ''' @foo def my_test(a): print("==my_test函数==", a) # 打印my_test函数,将看到实际上是bar函数 print(my_test) # .bar at 0x00000000021FABF8> # 下面代码看上去是调用my_test(),其实是调用bar()函数 my_test(10) my_test(6, 5)

上面程序定义了一个装饰器函数 foo,该函数执行完成后并不是返回普通值,而是返回 bar 函数(这是关键),这意味着被该 @foo 修饰的函数最终都会被替换成 bar 函数。

上面程序使用 @foo 修饰 my_test() 函数,因此程序同样会执行 foo(my_test),并将 my_test 替换成 foo() 函数的返回值:bar 函数。所以,上面程序第二行粗体字代码在打印 my_test 函数时,实际上输出的是 bar 函数,这说明 my_test 已经被替换成 bar 函数。接下来程序两次调用 my_test() 函数,实际上就是调用 bar() 函数。

运行上面程序,可以看到如下输出结果:

.bar at 0x000001C2A5953510>

===1=== (10,)

===2=== 90

my_test

==my_test函数== 90

***************

==my_test函数== 90

===1=== (6, 5)

===2=== 30

my_test

==my_test函数== 30

***************

==my_test函数== 30

通过 @ 符号来修饰函数是 Python 的一个非常实用的功能,它既可以在被修饰函数的前面添加一些额外的处理逻辑(比如权限检查),也可以在被修饰函数的后面添加一些额外的处理逻辑(比如记录日志),还可以在目标方法抛出异常时进行一些修复操作……这种改变不需要修改被修饰函数的代码,只要增加一个修饰即可。

上面介绍的这种在被修饰函数之前、之后、抛出异常后增加某种处理逻辑的方式,就是其他编程语言中的 AOP(Aspect Orient Progiuning,面向切面编程)。

下面例子示范了如何通过函数装饰器为函数添加权限检查的功能。程序代码如下:

def auth(fn): def auth_fn(*args): # 用一条语句模拟执行权限检查 print("----模拟执行权限检查----") # 回调要装饰的目标函数 fn(*args) return auth_fn @auth def test(a, b): print("执行test函数,参数a: %s, 参数b: %s" % (a, b)) # 调用test()函数,其实是调用装饰后返回的auth_fn函数 test(20, 15)

上面程序使用 @auth 修饰了 test() 函数,这会使得 test() 函数被替换成 auth() 函数所返回的 auth_fn 函数,而 auth_fn 函数的执行流程是:

先执行权限检查;

回调被修饰的目标函数。简单来说,auth_fn 函数就为被修饰函数添加了一个权限检查的功能。

运行该程序,可以看到如下输出结果:

—-模拟执行权限检查—-

执行test函数,参数a: 20, 参数b: 15

标签:bar,函数,Python,用法,器及,修饰,test,my,fn

python函数装饰器有什么用_Python @函数装饰器及用法(超级详细)相关推荐

  1. 【爆肝更新】Python基础教程:第五章_Python函数

    原创:公众号 数据说话 [爆肝更新]Python基础教程:第五章_Python函数 函数介绍 学习目标: 快速体验一下函数的使用 了解函数的作用. 函数:是组织好的,可重复使用的,用来实现特定功能的代 ...

  2. python关键字参数必须位于位置参数之前_python函数中的参数(关键字参数,默认参数,位置参数,不定长参数)...

    默认参数:定义函数的时候给定变量一个默认值. def num(age=1): 位置参数:调用函数的时候根据定义函数时的形参位置和实参位置进行引用. 关键字参数:如果定义的函数中含有关键字参数,调用函数 ...

  3. python中的object是什么意思_Python函数是所谓的第一类对象(First-Class Object)是什么鬼?...

    之前写过一篇关于装饰器的文章,虽然写得还算不错,但是也有不少同学表示没看懂,我大概分析了其中的原因,主要问题是他们不理解函数,因为Python中的函数不同于其它语言. 正确理解 Python函数,能够 ...

  4. python函数作用域包括局部变量和参数_python函数变量的作用域声明(全局变量和局部变量)...

    函数变量的作用域声明(全局变量和局部变量) 引入问题: 局部变量: 局部变量:定义在函数内部的变量,它的作用域也仅限于函数内部,出了函数就不能使用了. 例如: #encoding = utf-8 de ...

  5. python中、函数定义可以不包括以下_python函数定义精讲

    原标题:python函数定义精讲 在 Python 中,你也可以定义包含若干参数的函数.这里有三种可用的形式,也可以混合使用. 默认参数值 最常用的一种形式是为一个或多个参数指定默认值.这会创建一个可 ...

  6. python函数代码块以什么开头_Python 函数

    函数能提高应用的模块性,和代码的重复利用率. 定义一个函数: 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号(). 任何传入参数和自变量必须放在圆括号中间.圆括号之间可以用于定义参数. ...

  7. python的输出命令是什么意思_python输出语句print的用法是什么?_后端开发

    php异常处理函数有哪些_后端开发 php异常处理函数有"set_exception_handler",该函数用于设置用户自定义的异常处理函数,也用于创建运行期间的用户自己的异常处 ...

  8. python集合类型的四种操作符_python:集合【全用法】

    python中有列表.元组.集合.字典这四种可以存放多个数据元素的集合,他们在总体功能上都起着存放数据的作用,却都有着各自的特点.本片文章中我们会对集合的用法做详细说明. 演示环境: python3. ...

  9. python装饰器模式带参数_python函数装饰器、类装饰器和带参数的装饰器——装饰器模式...

    装饰器模式: 动态地给对象添加一些额外的职责,就增加功能来说,装饰模式比生产子类更加灵活 Component 是定义一个对象接口,可以给这些对象动态地添加职责.concreteComponent是定义 ...

最新文章

  1. C程序演示产生僵死进程的过程
  2. java虚拟机6.HotSpot的GC实现
  3. MySQL之alter和upate
  4. IPsec-×××基本技术挖掘
  5. ie bug(如果不足,留言大家一起分享)
  6. java和mysql中md5+base64的执行结果
  7. html中属性选择器是什么,为什么在CSS选择器/ HTML属性中首选使用破折号?
  8. springboot 排除 默认的loggback 和slf4j的依赖
  9. python连接mongodb的库文件pymongo
  10. [spark]Spark2.4.6用put写入写入Hbase1.3.1
  11. Linux下安装nodejs
  12. 饭卡管理系统学生E-R图
  13. ChromeCast Device Emulator的使用
  14. 洛谷 P3939 数颜色
  15. 用户下订单之后15分钟支付实现_用户提交订单,30分钟后没付款取消订单功能分析...
  16. 联发科:心态决定未来走势
  17. spring-retry框架使用说明
  18. 烤仔的朋友们 | 从薯片到卫生纸,进入NFT市场的10大传统品牌大盘点
  19. 柔性电子综述2012 ---在医疗,汽车行业,人机界面,移动设备以及其他场景下的可能应用
  20. android高级UI视频全套

热门文章

  1. 卢伟冰要宣布K50发布时间,竟遭市场部严厉制止!Redmi K50全系售价被曝
  2. 网络短视频内容审核趋严!短视频不得未经授权剪辑影视剧
  3. 造车梦又要“窒息”了?贾跃亭被美国认定骗局,收到退市警告!FF回应了......
  4. Reno7系列全球首发IMX709超感光猫眼镜头:OPPO/索尼联合打造
  5. 特斯拉第二季度生产20.6万辆电动汽车 交付20.1万辆
  6. 大型双标现场?摩托车举报特斯拉逆行反被罚
  7. iPhone 13系列又有新配色:猛男必看!
  8. 苹果高管谈论iPhone 12影像功能:硬件和软件的整体结合
  9. 蔚来汽车澄清“4年亏损57亿美元”说法:只有200亿人民币
  10. 原来“抖商大会”和抖音没有关系!抖音起诉“抖商大会”主办方 索赔300万