由于看python代码里面函数上的@ 不爽很久了,为了避免自己又忘记了这里来记录下。
简单总结:
@ 的作用就是在使用 @ 下面的函数(如下图的cs2)的时候,会在该函数执行前将该函数作为参数扔到@后跟着的处理函数先行处理(或预备处理)些东西,如下图的 time_cs 就是本文举的封装一个计时函数的装饰器的日志。
阅读下面代码对比使用语法糖的使用,即可了解。
本篇博客自参考b站大佬up的视频
一.预备的python常识
1.python中函数加括号与不加返回的区别
区分a()和a如下
一个是: 函数a在电脑中的十六进制地址
一个是: 函数a返回的值
def a():
return 1
print(a) # 函数a在电脑中的十六进制地址
print(a()) # 函数a返回的值
输出
<function a at 0x000001E37E1C6DC8>
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 time
def 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 time
def 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 num
def cs2():
print("\n")
print("*"*8 + "fun2" + "*"*8)
print("打印当前信息的测试函数,并返回一个整形的10")
print("*" * 20)
print("\n")
time.sleep(0.25)
return 10
def cs3(num):
print("cs3函数得到了cs2函数的输出:" + str(num))
cs3(time_cs(cs2))
输出
开始计时,打下时间戳
********fun2********
打印当前信息的测试函数,并返回一个整形的10
********************
完成计时,消耗时间0.257934
cs3函数得到了cs2函数的输出:10
进程已结束,退出代码 0
1.3.不使用装饰器的写法简单-2(有输入有输出)
即目标函数拥有有输入有输出返回值的情况,
1.3.1失败版
首先展示一个能运行但是,无法成功获得目标函数运行时间的写法,这里time_cs输出内部输出由result = func()改成了 result = func
import time
def 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 result
def cs2(num):
print("\n")
print("*"*8 + "fun2" + "*"*8)
print("打印当前信息的测试函数,并返回一个整形的" + str(num))
print("*" * 20)
print("\n")
time.sleep(0.25)
return num
def 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-06
cs3函数得到了cs2函数的输出:5
1.3.1成功版
import time
def 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 result
def cs2(num):
print("\n")
print("*"*8 + "fun2" + "*"*8)
print("打印当前信息的测试函数,并返回一个整形的" + str(num))
print("*" * 20)
print("\n")
time.sleep(0.25)
return num
def cs3(num):
print("cs3函数得到了cs2函数的输出:" + str(num))
num = time_cs(cs2,5)
cs3(num)
输出
开始计时,打下时间戳
********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************
传入的函数的id: 2832141314952
完成计时,消耗时间0.2586813
cs3函数得到了cs2函数的输出:5
1.3.不使用装饰器的写法简单-4(引入返回函数)
(目标函数拥有输入有输出)
这里将time_cs这个统计输入函数运行时间的功能函数,改造成输入和输出都是函数 的功能函数,这里是最接下面近装饰器写法的,建议与下面的2.使用装饰器的写法对比看
import time
import functools
def 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 result
return my_wrapper
def cs2(num):
print("\n")
print("*"*8 + "fun2" + "*"*8)
print("打印当前信息的测试函数,并返回一个整形的" + str(num))
print("*" * 20)
print("\n")
time.sleep(0.25)
return num
def 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.2594437
cs3函数得到了cs2函数的输出:5
传出的函数的id: 1392449545992
传出的函数的名字: my_wrapper
特别的加上 @functools.wraps(func) 以保证传输的函数名字不变
如下
import time
import functools
def 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 result
return my_wrapper
def cs2(num):
print("\n")
print("*"*8 + "fun2" + "*"*8)
print("打印当前信息的测试函数,并返回一个整形的" + str(num))
print("*" * 20)
print("\n")
time.sleep(0.25)
return num
def 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.2593284
cs3函数得到了cs2函数的输出:5
传出的函数的id: 2366053215688
传出的函数的名字: cs2
进程已结束,退出代码 0
2.使用装饰器的写法(有输入输出)
能够达到以上的效果最接近上面1.3,且使用起来更加的方便
import time
import functools
def 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 result
return 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 num
def cs3(num):
print("cs3函数得到了cs2函数的输出:" + str(num))
cs3(cs2(5))
print("传出的函数的名字: " + str(cs2.__name__)) # 不加 @functools.wraps(func) 就是输出my_wrapper
输出
开始计时,打下时间戳
********fun2********
打印当前信息的测试函数,并返回一个整形的5
********************
传入的函数的id: 2966589466664
完成计时,消耗时间0.26083870000000003
cs3函数得到了cs2函数的输出:5
传出的函数的名字: cs2
进程已结束,退出代码 0
三.装饰器其他使用应用记录
下班了后面来更新,使用。。。。
1.自动重启调用
import time
import functools
def 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,delay
while _tries > 1:
try:
return func(*args,**kwargs)
except exception:
print("程序错误" + str(_delay) + "秒后重新尝试")
time.sleep(_delay)
_tries -= 1
_delay *= backoff
return func(* args, ** kwargs)
return run_with_retry_police
return decorator_retry
视频27.58分说明