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分说明

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...