Python装饰器(一次搞清楚)

最重要的情绪管理是要明白,没有一种情绪是不应该的

一、简单装饰器

Python装饰器是一种语法糖,用于在不改变原有函数代码的情况下,为函数添加额外的功能。装饰器本质上是一个函数,它接收一个函数作为参数,并返回一个新的函数,通常使用@语法糖来应用装饰器。
1.装饰器本质是一个函数,可称之为函数装饰器;
2.装饰器也是一个闭包,即在非全局范围内定义的函数可以引用其外围空间中的变量
3.装饰器以一个函数作为参数,并且返回值也是一个函数;
4.装饰器不能修改被装饰的函数代码;不能修改被装饰函数的调用方式;
下面是一个简单的装饰器示例:

def my_decorator(func):def wrapper():print("Before the function is called.")res = func()print("After the function is called.")return resreturn wrapper@my_decorator
def say_hello():print("Hello!")# 调用被装饰的函数
say_hello()

在这个示例中,my_decorator是一个装饰器函数,它接收一个函数作为参数,并返回一个新的函数wrapper。wrapper函数包裹了原有的函数,它在调用原有函数之前和之后打印了额外的信息。@my_decorator语法糖将say_hello函数传递给my_decorator,并将其返回的新函数wrapper赋值给say_hello,这样调用say_hello函数时,实际上是调用了wrapper函数。执行结果为:
image.png

二、装饰器的运用场景

装饰器的应用非常广泛,可以用于实现各种功能,例如:

  1. 记录函数执行时间:通过在装饰器函数中记录函数执行的开始和结束时间,可以计算函数的执行时间。
  2. 缓存函数结果:通过在装饰器函数中维护一个缓存,可以避免重复计算相同参数的函数结果,提高函数的执行效率。
  3. 实现权限控制:通过在装饰器函数中检查用户的权限,可以控制用户对某些函数的访问权限。
  4. 日志记录:通过在装饰器函数中记录函数的输入参数和返回值,可以方便地进行调试和错误排除。
  5. 错误处理:通过在装饰器函数中捕获异常并进行处理,可以避免函数抛出异常导致程序崩溃。

Python装饰器是一种非常强大的语法糖,可以帮助我们实现各种功能,提高代码的复用性和可维护性。

三、运用案列

案列一:Python缓存cache实现

Python中的缓存(cache)机制可以通过装饰器来实现,但并不是所有的缓存都是通过装饰器实现的。装饰器是一种常用的实现缓存的方式,但是Python中还有其他的缓存实现方式,例如使用字典、使用缓存库等。
使用装饰器实现缓存的原理是,在装饰器函数中维护一个字典,将函数的输入参数作为键,函数的返回值作为值,存储在字典中。在每次调用函数之前,先检查字典中是否已经存在相同输入参数的缓存结果,如果存在,则直接返回缓存结果,否则调用原函数计算结果,并将计算结果缓存到字典中。
下面是一个简单的装饰器缓存示例:

def cache(func):cached_results = {}def wrapper(*args):if args in cached_results:return cached_results[args]result = func(*args)cached_results[args] = resultreturn resultreturn wrapper@cache
def fibonacci(n):if n <= 1:return n    return fibonacci(n-1) + fibonacci(n-2)print(fibonacci(10))

1.在这个示例中,cache是一个装饰器函数,它接收一个函数作为参数,并返回一个新的函数wrapper。wrapper函数维护了一个cached_results字典,用于存储函数的缓存结果。在调用被装饰的函数之前,wrapper函数先检查输入参数args是否已经存在于cached_results中,如果存在,则直接返回缓存结果,否则调用原函数计算结果,并将结果存储到cached_results中。
2.这个装饰器可以用于缓存计算复杂的函数,例如计算斐波那契数列的函数fibonacci。在第一次调用fibonacci(10)时,由于没有缓存结果,需要进行计算,计算结果存储到字典中。在后续的调用中,只需要从字典中获取结果即可,避免了重复计算,提高了程序的执行效率。
3.需要注意的是,装饰器缓存的实现方式可能存在一些问题,例如缓存数据的过期问题、内存占用问题等。因此,在实际使用中需要根据具体情况进行选择和调整,以确保程序的正确性和性能。

案列二:打印函数执行时间消耗

import time
# 注意:这个是带参数的装饰器
def calculate_execution_time(unit='s'):def decorator(func):def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)end_time = time.time()execution_time = end_time - start_timeif unit == 'ms':execution_time *= 1000print(f"函数 {func.__name__} 的执行时间为: {execution_time:.2f} 毫秒")else:print(f"函数 {func.__name__} 的执行时间为: {execution_time:.2f} 秒")return resultreturn wrapperreturn decorator@calculate_execution_time(unit='ms')
def my_function(a, b):# 假设这里是一个耗时的操作time.sleep(1)# 被装饰函数本身的功能print("{0} + {1} = ".format(a, b), a+b)my_function(1, 2)

1.上述代码定义了一个名为calculate_execution_time的装饰器,它接受一个可选的参数unit,用于指定执行时间的单位,默认为秒。装饰器内部定义了另一个函数decorator,它接受被装饰的函数func作为参数。在wrapper函数中,我们首先记录函数执行开始的时间戳start_time,然后调用被装饰的函数并获取其返回值result,最后计算函数执行所花费的时间,并根据unit参数选择合适的单位进行打印。
2.执行结果:
image.png
3.带参数的装饰器详见下文。

四、带参数的装饰器

Python装饰器可以用于装饰任何可调用对象,包括函数和类。当装饰函数带参数时,需要在装饰器函数里再定义一层函数来接收参数,这样才能将参数传递给被装饰的函数。
下面是一个简单的装饰器示例,演示了如何在装饰器函数中处理带参数的函数:

from functools import wraps
def repeat(num):def decorator(func):@wraps(func)def wrapper(*args, **kwargs):for i in range(num):print(f"Running function {func.__name__} for the {i+1} time")func(*args, **kwargs)return wrapperreturn decorator@repeat(3)
def greet(name):print(f"Hello, {name}!")greet("John")

1.在这个示例中,repeat是一个装饰器函数,它接收一个参数num,用于指定函数重复执行的次数。decorator是repeat的内部函数,它接收一个函数func作为参数,并返回一个新的函数wrapper。wrapper函数实现了函数的重复执行功能,它在循环中调用被装饰的函数func,并打印执行次数的信息。
另外,@wraps(func)用于保存函数的元信息,例如函数名、参数、注释等,可以使用functools模块中的wraps装饰器来保存被装饰函数的原信息。
2.wraps装饰器实际上是一个装饰器工厂函数,它接收一个函数作为参数,并返回一个新的装饰器函数。这个新的装饰器函数会将被装饰的函数替换为自己,并使用functools模块中的update_wrapper函数来将被装饰函数的元信息复制到新的装饰器函数中。
3.如果不使用wraps装饰器来保存被装饰函数的元信息,那么被装饰函数的元信息会被覆盖,例如函数名会变成wrapper,函数注释会变成装饰器函数的注释。
4.在应用装饰器时,使用@语法糖将装饰器函数repeat(3)应用到函数greet上,例如:

@repeat(3)
def greet(name):print(f"Hello, {name}!")

这样,每次调用greet函数时,都会执行3次,打印出执行的次数和函数的输出结果。
5.需要注意的是,在装饰器函数中定义的参数,需要在装饰器的每一层函数中进行传递和处理。在这个示例中,num参数在repeat函数中定义,被传递给decorator函数,最终在wrapper函数中使用。被装饰的函数的参数,需要在wrapper函数中定义为*args**kwargs,以支持任意数量和类型的参数,并在调用被装饰的函数时传递给它。
6.执行结果:
image.png

五、装饰器执行过程

像上面的函数,在代码执行时,会首先将**@cache**加载(函数定义时),执行cache中的内容,不执行wrapper的内容(函数调用时)。后续fibonacci函数执行时,会先执行对应的wrapper的内容,再执行函数本身,即cache中的cached_results类似于wrapper的全局变量
image.png
后续的多次调用中,cache中仅wrapper的内容在执行
image.png

六、装饰器的执行顺序

说法一:装饰顺序按由下到上,调用时由上到下执行顺序和装饰顺序相反。(简单记成正常的代码顺序即可)
说法二:装饰器由下到上依次立马执行,之后我们调用的f已经是被装饰器执行了之后的f了,此时是由上到下返回去依次调用。整个过程有点像先上楼梯(装饰过程),再下楼梯(调用函数)

def decorator_a(func):print('Get in decorator_a')def inner_a(*args, **kwargs):print('Get in inner_a')return func(*args, **kwargs)return inner_adef decorator_b(func):print('Get in decorator_b')def inner_b(*args, **kwargs):print('Get in inner_b')return func(*args, **kwargs)return inner_b@decorator_b
@decorator_a
def f(x):print('Get in f')return x * 2f(1)

执行结果:
image.png
关于执行顺序可参考:https://segmentfault.com/a/1190000007837364

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/153204.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【python】exec()内置函数释义

【python】exec内置函数释义 官方释义样例注意事项拓展感谢及参考博文 官方释义 官方Python API文档镇楼 exec(object, globalsNone, localsNone, /, *, closureNone) 支持动态执行 Python 代码&#xff0c; object 必须是字符串或者代码对象。 需要特别注意以下两点&#xff…

css自学框架之面板

面板是我们开发中经常用到&#xff0c;也就是页面版面中一块一块的板块&#xff0c;效果如下图&#xff1a; 一、css代码 .myth-panel {background-color: var(--white);border: solid 1px transparent;}.myth-panel .myth-panel-header {border-bottom: solid 1px transpar…

Play Beyond:Sui让优秀的游戏变得更好

自问世以来&#xff0c;视频游戏就紧随着文化产业发展。从Pong和Space Invaders的时代到Animal Crossing和Among Us&#xff0c;伟大的游戏总有能力吸引玩家&#xff0c;并推动娱乐产业发展。根据Grand View Research的数据&#xff0c;全球视频游戏市场在2022年估计为2170.6亿…

JavaScript进阶 第一天笔记

JavaScript 进阶 - 第1天 学习作用域、变量提升、闭包等语言特征&#xff0c;加深对 JavaScript 的理解&#xff0c;掌握变量赋值、函数声明的简洁语法&#xff0c;降低代码的冗余度。 理解作用域对程序执行的影响能够分析程序执行的作用域范围理解闭包本质&#xff0c;利用闭包…

核货宝:服装店收银系统如何选择?收银系统选购指南!

对于各行各业而言&#xff0c;收银系统都是必备的工具。特别是对于像服装店这样的零售门店来说&#xff0c;选择一套适合的收银系统尤为重要。在选择收银系统时&#xff0c;有一些关键的技巧需要注意&#xff0c;以达到软硬件合理搭配、节省开支的目的。下面将分享四个选购服装…

《视觉 SLAM 十四讲》第 7 讲 视觉里程计1 【如何根据图像 估计 相机运动】【特征点法】

github源码链接V2 文章目录 第 7 讲 视觉里程计17.1 特征点法7.1.1 特征点7.1.2 ORB 特征FAST 关键点 ⟹ \Longrightarrow ⟹ Oriented FASTBRIEF 描述子 7.1.3 特征匹配 7.2 实践 【Code】本讲 CMakeLists.txt 7.2.1 使用 OpenCV 进行 ORB 的特征匹配 【Code】7.2.2 手写 O…

智慧茶园:茶厂茶园监管可视化视频管理系统解决方案

一、方案背景 我国是茶叶生产大国&#xff0c;茶叶销量全世界第一。随着经济社会的发展和人民生活水平的提高&#xff0c;对健康、天然的茶叶产品的消费需求量也在逐步提高。茶叶的种植、生产和制作过程工序复杂&#xff0c;伴随着人力成本的上升&#xff0c;传统茶厂的运营及…

怒刷LeetCode的第25天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;闭合为环 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;动态规划 方法二&#xff1a;组合数学 方法三&#xff1a;递归 方法四&#xff1a;数学公式 第三题 题目来源 题目内容 解决方法 …

TCP原理特性详解

文章目录 可靠传输机制1.确认应答2.超时重传2.连接管理1.三次握手2.四次挥手 传输效率1.滑动窗口2.流量控制3.拥塞控制4.延时应答5.捎带应答 面向字节流粘包问题 TCP异常情况 可靠传输机制 可靠性&#xff1a;即发送方知道数据是发送成功了&#xff0c;还是失败了。 1.确认应答…

如何精细化管理嵌入式软件项目?ACT汽车电子与软件技术周演讲回顾

2023 ACT汽车电子与软件技术周已于8月18日在中国上海落下帷幕。展会现场&#xff0c;龙智技术支持部负责人、Atlassian认证专家叶燕秀与龙智技术工程师邱洁玉共同为观众带来了主题为“更好、更快、更安全&#xff1a;嵌入式开发中的最佳实践与工具链构建”的演讲&#xff0c;分…

elasticsearch内存占用详细分析

内存占用 ES的JVM heap按使用场景分为可GC部分和常驻部分。 可GC部分内存会随着GC操作而被回收&#xff1b; 常驻部分不会被GC&#xff0c;通常使用LRU策略来进行淘汰&#xff1b; 内存占用情况如下图&#xff1a; common space 包括了indexing buffer和其他ES运行需要的clas…

想要精通算法和SQL的成长之路 - 恢复二叉搜索树和有序链表转换二叉搜索树

想要精通算法和SQL的成长之路 - 恢复二叉搜索树和有序链表转换二叉搜索树 前言一. 恢复二叉搜索树二. 有序链表转换二叉搜索树 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 恢复二叉搜索树 原题链接 首先&#xff0c;一个正常地二叉搜索树在中序遍历下&#xff0c;遍历…

【2023研电赛】东北赛区一等奖作品:基于FPGA的小型水下无线光通信端机设计

本文为2023年第十八届中国研究生电子设计竞赛东北赛区一等奖作品分享&#xff0c;参加极术社区的【有奖活动】分享2023研电赛作品扩大影响力&#xff0c;更有丰富电子礼品等你来领&#xff01;&#xff0c;分享2023研电赛作品扩大影响力&#xff0c;更有丰富电子礼品等你来领&a…

312.戳气球

将戳气球转换到添加气球&#xff0c;记忆搜索slove(i,j)&#xff1a;在开区间(i,j)全部填满气球得到的最多硬币数&#xff0c;两端val[i]、val[j] class Solution { public:vector<vector<int>> ans;vector<int> val;int slove(int left,int right){if(left&…

【java基础学习】之DOS命令

#java基础学习 1.常用的DOS命令&#xff1a; dir:列出当前目录下的文件以及文件夹 md: 创建目录 rd:删除目录cd:进入指定目录 cd.. :退回到上级目录 cd\ : 退回到根目录 del:删除文件 exit:退出dos命令行 1.dir:列出当前目录下的文件以及文件夹 2.md: 创建目录 …

假期AI新闻热点:亚运会Al技术亮点;微软GPT-4V论文精读;Perplexity推出pplx-api;DALL-E 3多渠道测评 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f525; 科技感拉满&#xff0c;第19届杭州亚运会中的Al技术亮点 八年筹备&#xff0c;杭州第19届亚运会开幕式于9月23日晚隆重举行&#xff0…

输入电压转化为电流性 5~20mA方案

输入电压转化为电流性 5~20mA方案 方案一方案二方案三 方案一 XTR111是一款精密的电压-电流转换器是最广泛应用之一。原因有二&#xff1a;一是线性度非常好、二是价格便宜。总结成一点&#xff0c;就是性价比高。 典型电路 最终电路 Z1二极管处输出电流表达式&#xff1a;…

【Redis】基础数据结构-quicklist

Redis List 在Redis3.2版之前&#xff0c;Redis使用压缩列表和双向链表作为List的底层实现。当元素个数比较少并且元素长度比较小时&#xff0c;Redis使用压缩列表实现&#xff0c;否则Redis使用双向链表实现。 ziplist存在问题 不能保存过多的元素&#xff0c;否则查找复杂度…

Google-CTF-2016-Stego.pcap数据包解析

Google-CTF-2016&#xff08;a-cute-stegosaurus-100&#xff09; 前言&#xff1a;别人发的题目 随便看看 记录一下解题过程&#xff01; 知识点: 在报文段中有 6Bit 的状态控制码&#xff0c; 分别如下tcp URG&#xff1a;紧急比特&#xff08;urgent&#xff09;&#x…

echarts 中如何将 legend 设置成「直线」

Thinking系列&#xff0c;旨在利用10分钟的时间传达一种可落地的编程思想。 echarts 中如何将 legend 设置成「直线」&#xff1f; 所有图例均为直线 可以通过 itemHeight 和 itemWidth 来统一控制 legend: {itemHeight: 2,itemWidth: 20,data: [Expenses, Income]}部分是直线…