前言
decorator – 装饰器,返回值为另一个函数的函数,通常使用 @wrapper 语法形式来进行函数变换。 装饰器的常见例子包括 classmethod() 和 staticmethod()。
装饰器语法只是一种语法糖,以下两个函数定义在语义上完全等价:
1 2 3 4 5 6 7
| def f(arg): ... f = staticmethod(f)
@staticmethod def f(arg): ...
|
源教程地址: https://blog.csdn.net/weixin_44992737/article/details/125868592 .
本文的源代码仓库: https://github.com/LuYF-Lemon-love/susu-python-packages-notes/tree/main/04_decorator .
操作系统:Windows 10 专业版
参考文档
- python装饰器详解
- decorator – 装饰器
- 函数定义
装饰器的官方定义
装饰器本质上是一个Python函数(其实就是闭包),它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。装饰器用于有以下场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。
给某个函数加上一个装饰器
一般写法
- 新建文件
01-ordinary.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import time import threading def how_much_time(func): def inner(): t_start = time.time() func() t_end = time.time() print("一共花费了{0}秒时间".format(t_end - t_start)) return inner def sleep_5s(): time.sleep(5) print("%d秒结束了" % (5)) def sleep_6s(): time.sleep(6) print("%d秒结束了" % (6)) sleep_5s = how_much_time(sleep_5s) sleep_6s = how_much_time(sleep_6s) t1 = threading.Thread(target=sleep_5s) t2 = threading.Thread(target=sleep_6s) t1.start() t2.start()
|
- 运行:
1 2 3 4 5 6
| $ python 01-ordinary.py 5秒结束了 一共花费了5.002536058425903秒时间 6秒结束了 一共花费了6.000275373458862秒时间 $
|
装饰器写法
- 新建文件
02-decorator.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import time import threading def how_much_time(func): def inner(): t_start = time.time() func() t_end = time.time() print("一共花费了{0}秒时间".format(t_end - t_start, )) return inner @how_much_time def sleep_5s(): time.sleep(5) print("%d秒结束了" % (5)) @how_much_time def sleep_6s(): time.sleep(6) print("%d秒结束了" % (6)) t1 = threading.Thread(target=sleep_5s) t2 = threading.Thread(target=sleep_6s) t1.start() t2.start()
|
- 运行:
1 2 3 4 5 6 7
| $ python 02-decorator.py 5秒结束了 一共花费了5.001528263092041秒时间 6秒结束了 一共花费了6.000848054885864秒时间
$
|
给一个函数添加两个装饰器
- 新建
03-two-decorators.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import time import threading def how_much_time(func): print("how_much_time 外层开始") def inner(): print("how_much_time 内层开始") t_start = time.time() func() t_end = time.time() print("一共花费了{0}秒时间".format(t_end - t_start)) print("how_much_time 内层结束") print("how_much_time 外层结束") return inner def mylog(func): print("mylog 外层开始") def inner(): print("mylog 内层开始") func() print("mylog 内层结束") print("mylog 外层结束") return inner @mylog @how_much_time def sleep_5s(): print("sleep_5s 开始") time.sleep(5) print("%d秒结束了" % (5)) print("sleep_5s 结束") sleep_5s()
|
- 运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| $ python 03-two-decorators.py how_much_time 外层开始 how_much_time 外层结束 mylog 外层开始 mylog 外层结束 mylog 内层开始 how_much_time 内层开始 sleep_5s 开始 5秒结束了 sleep_5s 结束 一共花费了5.002262592315674秒时间 how_much_time 内层结束 mylog 内层结束
$
|
带参数装饰器
- 新建
04-passing-parameters.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import time
def how_much_time(func): def inner(*args, **kwargs): t_start = time.time() func(*args, **kwargs) t_end = time.time() print("一共花费了{0}秒时间".format(t_end - t_start, )) return inner @how_much_time def sleep_ns(n): time.sleep(n) print("%d秒结束了" % (5,))
def mylog(type): def decorator(func): def inner(*args, **kwargs): if type == "文件": print("文件中: 日志记录") else: print("控制台: 日志记录") func(*args, **kwargs) return inner return decorator
@mylog("文件") def func(a, b): print("func: ", a, b)
sleep_ns(5) print("*" * 42) func(100, 200)
|
- 运行:
1 2 3 4 5 6 7 8
| $ python 04-passing-parameters.py 5秒结束了 一共花费了5.000115871429443秒时间 ****************************************** 文件中: 日志记录 func: 100 200
$
|
wraps语法糖
因为装饰器实质是就是一个函数,是一个被修饰过函数,它与原来未被修饰的函数是两个不同的函数对象。所以,这个装饰器丢失了原来函数对象的一些属性,比如:__name__
,__doc__
等属性。使用wraps
语法糖可以保留这些属性。
- 新建
05-wraps.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import time from functools import wraps
def how_much_time(func): def inner(*args, **kwargs): t_start = time.time() func(*args, **kwargs) t_end = time.time() print("函数文档:", func.__doc__) print("一共花费了{0}秒时间".format(t_end - t_start)) return inner @how_much_time def sleep_ns_time(n): """sleep_ns_time 的文档""" time.sleep(n) print("%d秒结束了" % (5,))
def mylog(func): @wraps(func) def inner(*args, **kwargs): func(*args, **kwargs) print("函数文档(wraps):", func.__doc__) print("日志记录(wraps)...") return inner
@mylog def sleep_ns_log(n): """sleep_ns_log 的文档(wraps)""" time.sleep(n) print("%d秒结束了" % (5,))
sleep_ns_time(5) print("函数文档--->", sleep_ns_time.__doc__) print("*" * 42) sleep_ns_log(5) print("函数文档(wraps)--->", sleep_ns_log.__doc__)
|
- 运行:
1 2 3 4 5 6 7 8 9 10 11 12
| $ python 05-wraps.py 5秒结束了 函数文档: sleep_ns_time 的文档 一共花费了5.000011920928955秒时间 函数文档---> None ****************************************** 5秒结束了 函数文档(wraps): sleep_ns_log 的文档(wraps) 日志记录(wraps)... 函数文档(wraps)---> sleep_ns_log 的文档(wraps)
$
|
类装饰器
类装饰器这个写法,主要思路就是返回一个增加了新功能的函数对象,只不过这个函数对象是一个类的实例对象。由于装饰器是可调用对象,所以必须在类里面实现__call__
方法,这样由类生成的各种实例加上()
就可以运行了。
不带参数的类装饰器
- 新建
06-class-decorator.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import time
class Decorator: def __init__(self, func): self.func = func def defer_time(self): time.sleep(5) print("延时结束了") def __call__(self, *args, **kwargs): self.defer_time() self.func()
@Decorator def f1(): print("延时之后我才开始执行")
f1()
|
- 运行:
1 2 3 4 5
| $ python 06-class-decorator.py 延时结束了 延时之后我才开始执行
$
|
带参数的类装饰器
- 新建
07-class-passing-parameters.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import time class Decorator: def __init__(self, func): self.func = func def defer_time(self,time_sec): time.sleep(time_sec) print(f"{time_sec}s延时结束了") def __call__(self, time): self.defer_time(time) self.func() @Decorator def f1(): print("延时之后我才开始执行") f1(5)
|
- 运行:
1 2 3 4 5
| $ python 07-class-passing-parameters.py 5s延时结束了 延时之后我才开始执行
$
|
结语
第八十三篇博文写完,开心!!!!
今天,也是充满希望的一天。