python @ 装饰器(修饰器,语法糖)使用与不使用对比总结记录
创始人
2024-03-19 12:19:58
0

由于看python代码里面函数上的@ 不爽很久了,为了避免自己又忘记了这里来记录下。
简单总结:
@ 的作用就是在使用 @ 下面的函数(如下图的cs2)的时候,会在该函数执行前将该函数作为参数扔到@后跟着的处理函数先行处理(或预备处理)些东西,如下图的 time_cs 就是本文举的封装一个计时函数的装饰器的日志。

阅读下面代码对比使用语法糖的使用,即可了解。
本篇博客自参考b站大佬up的视频
在这里插入图片描述
在这里插入图片描述

一.预备的python常识

1.python中函数加括号与不加返回的区别

区分a()和a如下
一个是: 函数a在电脑中的十六进制地址
一个是: 函数a返回的值

def a():return 1print(a) # 函数a在电脑中的十六进制地址
print(a()) # 函数a返回的值

输出


1

2.在python中函数可以作为参数直接传入


def a():return "a的返回"def b():return "b的返回"def out(action):"""传入函数对象"""print(action())out(a)
out(b)

输出

a的返回
b的返回

二. 计时函数的例子说明

1.1 不使用装饰器的写法简单-0(无输入输出)

即检测的目标函数没有输入值也没有返回值的情况

import timedef time_cs(func):""":param func: 传入的你需要计时的函数:return: 无"""print("开始计时,打下时间戳")start = time.perf_counter()func()end = time.perf_counter()print("完成计时,消耗时间" + str(end-start))print("\n")def cs():print("\n")print("*"*8 + "fun" + "*"*8)print("只是打印当前信息的测试函数")print("*" * 20)print("\n")time.sleep(0.25)time_cs(cs)

输出

开始计时,打下时间戳********fun********
只是打印当前信息的测试函数
********************完成计时,消耗时间0.2602558

1.2 不使用装饰器的写法简单-1(只有输出)

即目标函数拥有返回值的情况
这里比较麻烦需要在time_cs 函数中将返回值用一个变量存储,使得你需要预先知道目标函数返回值的个数

import timedef time_cs(func):""":param func: 传入的你需要计时的函数:return: func函数的输出"""print("开始计时,打下时间戳")start = time.perf_counter()num = func()end = time.perf_counter()print("完成计时,消耗时间" + str(end-start))print("\n")return numdef cs2():print("\n")print("*"*8 + "fun2" + "*"*8)print("打印当前信息的测试函数,并返回一个整形的10")print("*" * 20)print("\n")time.sleep(0.25)return 10def cs3(num):print("cs3函数得到了cs2函数的输出:" + str(num))cs3(time_cs(cs2))

输出

开始计时,打下时间戳********fun2********
打印当前信息的测试函数,并返回一个整形的10
********************完成计时,消耗时间0.257934cs3函数得到了cs2函数的输出:10进程已结束,退出代码 0

1.3.不使用装饰器的写法简单-2(有输入有输出)

即目标函数拥有有输入有输出返回值的情况,

1.3.1失败版

首先展示一个能运行但是,无法成功获得目标函数运行时间的写法,这里time_cs输出内部输出由result = func()改成了 result = func

import timedef time_cs(func):""":param func: 传入的你需要计时的函数:return: func函数的输出"""print("开始计时,打下时间戳")start = time.perf_counter()result = func # 注意这里!!不是 func()!!print("传入的函数的id: " + str(id(func)))end = time.perf_counter()print("完成计时,消耗时间" + str(end - start))print("\n")return resultdef cs2(num):print("\n")print("*"*8 + "fun2" + "*"*8)print("打印当前信息的测试函数,并返回一个整形的" + str(num))print("*" * 20)print("\n")time.sleep(0.25)return numdef cs3(num):print("cs3函数得到了cs2函数的输出:" + str(num))num = time_cs(cs2(5))
cs3(num)

输出如下,可以看到该写法下的消耗时间统计是错误的
原因:传入的时候就是传入了一个 cs2(5) 即一个完成了执行了time.sleep(0.25) 之后的程序进入,因此下面输出的9.900000000007125e-06 代表的时间实际就可以理解成输入的func函数运行时间为0时,time_cs函数自身的运行时间

********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************开始计时,打下时间戳
传入的函数的id: 140720369607200
完成计时,消耗时间9.900000000007125e-06cs3函数得到了cs2函数的输出:5

1.3.1成功版

import timedef time_cs(func,num):""":param func: 传入的你需要计时的函数:return: func函数的输出"""print("开始计时,打下时间戳")start = time.perf_counter()result = func(num)print("传入的函数的id: " + str(id(func)))end = time.perf_counter()print("完成计时,消耗时间" + str(end - start))print("\n")return resultdef cs2(num):print("\n")print("*"*8 + "fun2" + "*"*8)print("打印当前信息的测试函数,并返回一个整形的" + str(num))print("*" * 20)print("\n")time.sleep(0.25)return numdef cs3(num):print("cs3函数得到了cs2函数的输出:" + str(num))num = time_cs(cs2,5)
cs3(num)

输出

开始计时,打下时间戳********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************传入的函数的id: 2832141314952
完成计时,消耗时间0.2586813cs3函数得到了cs2函数的输出:5

1.3.不使用装饰器的写法简单-4(引入返回函数)

(目标函数拥有输入有输出)
这里将time_cs这个统计输入函数运行时间的功能函数,改造成输入和输出都是函数 的功能函数,这里是最接下面近装饰器写法的,建议与下面的2.使用装饰器的写法对比看

import time
import functoolsdef time_cs(func):""":param func: 传入的你需要计时的函数:return: func函数的输出"""def my_wrapper(*args, **kwargs):"""再创建一个装饰器函数"""print("开始计时,打下时间戳")start = time.perf_counter()result = func(*args, **kwargs)print("传入的函数的id: " + str(id(func)))end = time.perf_counter()print("完成计时,消耗时间" + str(end - start))print("\n")return resultreturn my_wrapperdef cs2(num):print("\n")print("*"*8 + "fun2" + "*"*8)print("打印当前信息的测试函数,并返回一个整形的" + str(num))print("*" * 20)print("\n")time.sleep(0.25)return numdef cs3(num):print("cs3函数得到了cs2函数的输出:" + str(num))cs2_2 = time_cs(cs2)
cs3(cs2_2(5))
print("传出的函数的id: " + str(id(cs2_2)))
print("传出的函数的名字: " + str(cs2_2.__name__)) # 不加 @functools.wraps(func) 就是输出my_wrapper

输出

开始计时,打下时间戳********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************传入的函数的id: 1392449547432
完成计时,消耗时间0.2594437cs3函数得到了cs2函数的输出:5
传出的函数的id: 1392449545992
传出的函数的名字: my_wrapper

特别的加上 @functools.wraps(func) 以保证传输的函数名字不变

如下

import time
import functoolsdef time_cs(func):""":param func: 传入的你需要计时的函数:return: func函数的输出"""@functools.wraps(func) # 这里的作用是使得返回的 my_wrapper 的 .__name__ 名字# 和传入的 func.__name__  的相同,如下print(cs2_2.__name__)def my_wrapper(*args, **kwargs):"""再创建一个装饰器函数"""print("开始计时,打下时间戳")start = time.perf_counter()result = func(*args, **kwargs)print("传入的函数的id: " + str(id(func)))end = time.perf_counter()print("完成计时,消耗时间" + str(end - start))print("\n")return resultreturn my_wrapperdef cs2(num):print("\n")print("*"*8 + "fun2" + "*"*8)print("打印当前信息的测试函数,并返回一个整形的" + str(num))print("*" * 20)print("\n")time.sleep(0.25)return numdef cs3(num):print("cs3函数得到了cs2函数的输出:" + str(num))cs2_2 = time_cs(cs2)
cs3(cs2_2(5))
print("传出的函数的id: " + str(id(cs2_2)))
print("传出的函数的名字: " + str(cs2_2.__name__)) # 不加 @functools.wraps(func) 就是输出my_wrapper

输出

开始计时,打下时间戳********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************传入的函数的id: 2366053213960
完成计时,消耗时间0.2593284cs3函数得到了cs2函数的输出:5
传出的函数的id: 2366053215688
传出的函数的名字: cs2进程已结束,退出代码 0

2.使用装饰器的写法(有输入输出)

能够达到以上的效果最接近上面1.3,且使用起来更加的方便

import time
import functoolsdef time_cs(func):""":param func: 传入的你需要计时的函数:return: func函数的输出"""@functools.wraps(func) # 这里的作用是使得返回的 my_wrapper 的 .__name__ 名字# 和传入的 func.__name__  的相同,如下print(cs2_2.__name__)def my_wrapper(*args, **kwargs):"""再创建一个装饰器函数"""print("开始计时,打下时间戳")start = time.perf_counter()result = func(*args, **kwargs)print("传入的函数的id: " + str(id(func)))end = time.perf_counter()print("完成计时,消耗时间" + str(end - start))print("\n")return resultreturn my_wrapper@time_cs
def cs2(num):print("\n")print("*"*8 + "fun2" + "*"*8)print("打印当前信息的测试函数,并返回一个整形的" + str(num))print("*" * 20)print("\n")time.sleep(0.25)return numdef cs3(num):print("cs3函数得到了cs2函数的输出:" + str(num))cs3(cs2(5))print("传出的函数的名字: " + str(cs2.__name__)) # 不加 @functools.wraps(func) 就是输出my_wrapper

输出

开始计时,打下时间戳********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************传入的函数的id: 2966589466664
完成计时,消耗时间0.26083870000000003cs3函数得到了cs2函数的输出:5
传出的函数的名字: cs2进程已结束,退出代码 0

三.装饰器其他使用应用记录

下班了后面来更新,使用。。。。

1.自动重启调用

import time
import functoolsdef retry_and_show(exception: Exception, tries: int = 3, delay: int = 1, backoff: int = 2):""":param exception: 错误;类型:param tries: 重试次数:param delay: 间隔重试时间间隔 秒:param backoff: 回退步长:return:"""def decorator_retry(func):@functools.wraps(func)def run_with_retry_police(*args,**kwargs):_tries, _delay = tries,delaywhile _tries > 1:try:return func(*args,**kwargs)except exception:print("程序错误" + str(_delay) + "秒后重新尝试")time.sleep(_delay)_tries -= 1_delay *= backoffreturn func(* args, ** kwargs)return run_with_retry_policereturn decorator_retry

视频27.58分说明

相关内容

热门资讯

保存时出现了1个错误,导致这篇... 当保存文章时出现错误时,可以通过以下步骤解决问题:查看错误信息:查看错误提示信息可以帮助我们了解具体...
汇川伺服电机位置控制模式参数配... 1. 基本控制参数设置 1)设置位置控制模式   2)绝对值位置线性模...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
表格中数据未显示 当表格中的数据未显示时,可能是由于以下几个原因导致的:HTML代码问题:检查表格的HTML代码是否正...
本地主机上的图像未显示 问题描述:在本地主机上显示图像时,图像未能正常显示。解决方法:以下是一些可能的解决方法,具体取决于问...
表格列调整大小出现问题 问题描述:表格列调整大小出现问题,无法正常调整列宽。解决方法:检查表格的布局方式是否正确。确保表格使...
不一致的条件格式 要解决不一致的条件格式问题,可以按照以下步骤进行:确定条件格式的规则:首先,需要明确条件格式的规则是...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...