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

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...