当前位置: 首页 > news >正文

python类装饰器应用_如何理解下面python代码装饰器的应用?

装饰器是python里的一个非常有意思的部分,他用于封装函数代码,显式的将封装器应用到被封装的函数上,从而使得他们选择加入到装饰器指定的功能中。对于在函数运行前处理常见前置条件(例如确认授权),或在函数运行后确保清理(输出清除或异常处理),装饰器都非常有用。

是不是感觉听不明白,太绕了!

简单来说,装饰器就是实现了一个通用的功能,然后将这个通用的功能应用到不同的、需要使用这个功能的函数上,从而避免每次都在不同函数上反复写相同的功能代码。

装饰器的本质是一个函数,他接受被装饰的函数作为位置参数,装饰器通过使用该参数来执行某些操作,然后返回一个函数引用,这个函数可以是原始函数,或者是另外一个函数。

我们举例子说明,装饰器是这样的函数,他们接受被装饰的可调用函数作为唯一的参数,并且返回一个可调用函数,

registry = []

def register(decorated):

registry.append(decorated)

return decorated

def foo():

return 3

foo = register(foo)

print(registry[0])

register方法是一个简单的装饰器,它把被装饰的函数添加到一个列表中,然后这里是将未改变的被装饰函数返回,可以看出,装饰器一般是传入被装饰函数的引用,然后经过一些指定的处理,最后返回值也是一个函数引用。

还有一种更简单的语法形式:

装饰器的语法糖:我们这里看到的对foo进行装饰的方法是运用

foo = register(foo)语句,还有一种简单的用法是在声明函数的位置应用装饰器,从而使得代码更容易阅读,并且让人立刻意识到使用了装饰器

registry = []

def register(decorated):

registry.append(decorated)

return decorated

@register

def foo(x=3):

return x

@register

def bar(x=5):

return 5

for func in registry:

print(func())

3

5

再看一个更复杂、更一般化的装饰器函数。装饰器的本质是在执行原有函数(被装饰的函数)的同时,再加上一些额外的功能。

def requires_ints(decorated):

def inner(*args, **kwargs):

kwarg_values = [i for i in kwargs.values()]

for arg in list(args) + kwarg_values:

if not isinstance(arg, int):

raise TypeError('{}only accepts integers as arguments'.format(decorated.__name__))

return decorated(*args, **kwargs)

return inner

在这个装饰器函数requires_ints我们可以看到,他定义了一个内嵌函数inner,这个内嵌函数的参数首先收集被装饰函数的所有参数,然后对其进行判断,判断其是否为整数类型(这就是装饰器添加的额外功能),然后再调用被装饰的函数本身,最后将这个内嵌函数返回。因此当我们再用原函数名进行调用的时候,原来的被装饰函数的引用就能指向这个新的内嵌函数,就能在实现原函数功能的基础上,加上附加的功能了。

同时,我们再提炼一下这里面的几个重难点:

第一,requires_ints中,decorated这个变量是内嵌作用域的变量,在他调用退出后,返回的inner函数是可以记住这个变量的。

第二,python不支持函数的参数列表的多态,即一个函数名只能对应唯一的参数列表形式。

第三,在内嵌函数内部调用被装饰函数的时候,使用了解包参数,关于这*args, **kwargs,的参数形式,前面章节中细讲过。

那我们也用这个装饰器来装饰一个函数。

@requires_ints

def foo(x,y):

print(x+y)

foo(3,5)

8

这里将名称foo赋给inner函数,而不是赋给原来被定义的函数,如果运行foo(3,5),将利用传入的这两个参数运行inner函数,inner函数执行类型检查,然后运行被装饰方法,如果传入的不是整形数,例如下面这个例子,那么装饰器的附加功能就会进行类型检查:

@requires_ints

def foo(x,y):

print(x+y)

foo('a',5)

Traceback (most recent call last):

File "E:/12homework/12homework.py", line 15, in

foo('a',5)

File "E:/12homework/12homework.py", line 7, in inner

raise TypeError('{}only accepts integers as arguments'.format(decorated.__name__))

TypeError: foo only accepts integers as arguments

其次内嵌的函数和被装饰的函数的参数形式必须完全一样,这里用的*args, **kwargs概况函数参数的一般形式,因此也是完全对应的。

最后说说装饰器参数

最后来介绍这个复杂一些的话题,装饰器参数。之前我们列举的常规例子里,装饰器只有一个参数,就是被装饰的方法。但是,有时让装饰器自身带有一些需要的信息,从而使装饰器可以用恰当的方式装饰方法十分有用。

这些参数并不是和被装饰的函数并列作为参数签名,而是在原有装饰器的基础上额外再增加一层封装,那么,实质是这个接受其他参数的装饰器并不是一个实际的装饰器,而是一个返回装饰器的函数。

最终返回的内嵌函数inner是最终使用indent和sort_keys参数的函数,这没有问题

import json

def json_output(indent=None, sort_keys=False):

def actual_decorator(decorated):

def inner(*args, **kwargs):

result = decorated(*args, **kwargs)

return json.dumps(result, indent=indent, sort_keys=sort_keys)

return inner

return actual_decorator

@json_output(indent=8)

def do_nothing():

return {'status':'done','func':'yes'}

print(do_nothing())

{

"status": "done",

"func": "yes"

}

我们在这里详细解释说明的是操作顺序,看上去我们使用的是@json_output(indent=8),作这和之前的装饰器语法糖看上去有些不同,实际上这个不是最终的装饰器函数,通过调用json_output(indent=8),返回函数指针actual_decorator,这个函数才是真正放在@后的装饰器函数,原始的被装饰函数最终获得了内涵更丰富的inner函数对象,完成了装饰过程,值得一提的是,所谓的装饰器参数最终传给了最内层的inner函数。

记住,在定义装饰器函数后,真正的装饰器函数只有一个参数,那就是被装饰的函数指针,而有其他参数的函数实质上只是装饰器的外围函数,他可以依据参数对装饰器进行进一步的定制。一句话:一个函数不可能接受被装饰的方法,又接受其他参数

在语法糖中@func这种不带括号的,就是直接使用装饰器函数进行装饰,如果是@func()带括号的,实质上是先调用func()函数返回真正的装饰器,然后再用@进行调用。

关于数据科学更系统、更深入的探讨可进入我们的专栏《Python数据科学之路》:酱油哥:来吧,一起踏上Python数据科学之路​zhuanlan.zhihu.com

本专栏模仿美剧剧集编排分为五季,第一季:Python编程语言核心基础、第二季:Python数据分析基本工具、第三季:Python语言描述的数学基础、第四季:机器学习典型算法专题、第五季:实战热点深度应用。

相关文章:

  • W3C标准的HTML标签--按功能类别排列
  • 服务器压力测试_【学习记录】第一篇:性能测试基础
  • ORA-02068: 以下严重错误源于 TO_XXXX.DW ORA-03135: 连接失去联系 问题的解决
  • python数列求和1+11+111前n项_Leetcode Python算法(11)-----外观数列
  • TFS(Team Foundation Server)介绍和入门
  • mac svn工具_SmartSVN 12 for Mac(SVN客户端)
  • SharePoint Web Service系列:进行列表的增删改
  • python蜂窝状六边形_画多层六边形(小区),蜂窝状,matlab
  • SharePoint Web Service系列: Add或Update类型为User的项
  • 哲学家就餐问题python_Python实现哲学家就餐问题实例代码
  • SharePoint Web Service系列:获取文档的属性信息
  • 如何设置两个textview 的内容在listview同一行里_西门子触摸屏如何与S7-1200/1500的PLC-SIM离线仿真
  • SharePoint Web Service系列:编写自定义SharePoint Web Services之一
  • 无线鼠标可以强制配对_一款简单的小设备,吉选gesobyte无线鼠标
  • oracle sql查询时间_「Burpsuite练兵场」SQL注入及相关实验(二)
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • Android开发 - 掌握ConstraintLayout(四)创建基本约束
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • Django 博客开发教程 16 - 统计文章阅读量
  • JavaScript 基础知识 - 入门篇(一)
  • leetcode-27. Remove Element
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • OSS Web直传 (文件图片)
  • 编写高质量JavaScript代码之并发
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 判断客户端类型,Android,iOS,PC
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 前端性能优化--懒加载和预加载
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • 原生js练习题---第五课
  • 阿里云服务器购买完整流程
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (1)STL算法之遍历容器
  • (175)FPGA门控时钟技术
  • (Matalb时序预测)PSO-BP粒子群算法优化BP神经网络的多维时序回归预测
  • (rabbitmq的高级特性)消息可靠性
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (离散数学)逻辑连接词
  • (一)kafka实战——kafka源码编译启动
  • (转) Face-Resources
  • .chm格式文件如何阅读
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .NET Core中的去虚
  • .net 程序 换成 java,NET程序员如何转行为J2EE之java基础上(9)
  • .NET 解决重复提交问题
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?