Python装饰器
1. 初始装饰器
1.1. 装饰器简介
装饰器(decorator)就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。
装饰器的功能特点:
- 不修改已有函数的源代码
- 不修改已有函数的调用方式
- 给已有函数增加额外的功能
语法糖:
@xx_decorator
注意点:
- 装饰器有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。
1.2. 装饰器的实现原理(闭包)
def set_func(func):
def call_func():
print("------增加的内容------")
func()
return call_func
def demo():
print("------demo------")
# 不修改已有函数的源代码
# 不修改已有函数的调用方式
# 给已有函数增加额外的功能
demo = set_func(demo)
demo()
1.3. 使用语法糖@xx_decorator
之后的装饰器示例
只是把 demo = set_func(demo)
通过语法糖 @set_func
简化了!
def set_func(func):
def call_func():
print("------增加的内容------")
func()
return call_func
@set_func # 相当于 demo = set_func(demo)
def demo():
print("------demo------")
demo()
1.4. 一个不带参装饰器格式示例
def decorator(func): # func:被装饰的目标函数.
def call_func():
func() # 执行被装饰的目标函数
return call_func
1.5. 装饰器的执行时间
- 直接执行当前模块时解释器遇到装饰器
@xx_decorator
的时候,装饰器开始执行 - 作为模块导入时,装饰器在加载模块(解释器遇到
@xx_decorator
)时执行
def set_func(func):
print("装饰器开始装饰")
def call_func():
print("------增加的内容------")
func()
return call_func
@set_func
def demo():
print("------demo------")
2. 通用装饰器
装饰器的内部函数的结构和被装饰的函数结构应是一致的
- 被装饰函数有参,那么装饰器内部函数的参数结构和被装饰函数的结构是一致的
- 被装饰函数有返回值,那么装饰器的内部函数也需要有返回值
通用装饰器示例:
装饰器使用不定长参数,无论被装饰函数是否带参都可以兼容,
装饰器直接把被装饰函数的返回值返回(无返回值使用默认的None)
def set_func(func):
def call_func(*args, **kwargs):
print("------增加的内容------")
return func(*args, **kwargs) # 这里的 * 是拆包,接收被装饰函数的返回值后返回
return call_func
@set_func
def demo(*args, **kwargs):
print("------demo------")
print(f"args: {args}")
print(f"kwargs: {kwargs}")
return "im demo"
ret = demo(100, 200) # 这里的ret是call_func函数的返回值
print(ret)
3. 多个装饰器一起使用
3.1. 多个装饰器使用说明
@set_func2
@set_func1
def demo():
pass
- 装饰器装饰顺序是由下到上(靠近被装饰函数的装饰器被先调用)。
解释器会先使用set_func1对demo函数进行装饰,然后再使用set_func2对set_func1的返回值进行装饰。
- 被装饰函数被调用的时候,装饰器执行顺序是由上到下(靠近被装饰函数的装饰器内容最晚调用)。
执行demo函数的时候,set_func2的内容会先执行,然后再执行set_func1的内容。
3.1. 示例
def set_func1(func):
print("set_func1执行")
def call_func1(*args, **kwargs):
print("------装饰器1增加的内容------")
return func(*args, **kwargs) # 这里的 * 是拆包,接收被装饰函数的返回值后返回
return call_func1
def set_func2(func):
print("set_func2执行")
def call_func2(*args, **kwargs):
print("------装饰器2增加的内容------")
return func(*args, **kwargs) # 这里的 * 是拆包,接收被装饰函数的返回值后返回
return call_func2
@set_func2 # 对demo进行装饰(这里的demo是被set_func1装饰后的demo, 实际上是call_func1), demo = set_func2(call_func1), set_func2的返回值是 call_func2
@set_func1 # 对demo进行装饰,demo = set_func1(demo), set_func1的返回值是 call_func1
def demo(*args, **kwargs):
print("------demo------")
return "demo ok"
ret = demo(100, 200) # 这里的demo实际上是执行的call_func2
print(ret)
装饰过程(由下到上):
- 解释器遇到@set_func2,但是@set_func2的下一行不是函数,无法被装饰,不处理继续向下执行;
- 解释器遇到@set_func1,@set_func1下是被装饰函数demo,set_func1开始对demo进行装饰,返回被装饰后的demo函数(即set_func1中的内部函数call_func1);
- 此时解释器开始使用set_func2对最新的demo函数(即set_func1中的内部函数call_func1)进行装饰,返回被装饰后的demo(即set_func2中的内部函数call_func2)。
执行过程(由上到下):
- 解释器遇到
ret = demo(100, 200)
开始执行demo函数。此时的demo函数其实是set_func2中的内部函数call_func2; - call_func2中遇到func函数,func是set_func1中的内部函数call_func1;
- call_func1中遇到func函数,此时的func函数就是一开始被装饰的demo;
- 开始执行demo函数。
结果:
set_func1执行
set_func2执行
------装饰器2增加的内容------
------装饰器1增加的内容------
------demo------
demo ok
4. 类装饰器
4.1. call方法实现
- 想要让类的实例对象能够像函数一样进行调用,需要在类里面使用
__call__()
方法,把类的实例变成可调用对象(callable) - 类装饰器装饰函数功能在
__call__()
方法里面进行添加
通用类装饰器:
class Test:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("装饰器功能")
return self.func(*args, **kwargs)
@Test # demo = Test(demo)
def demo():
return "haha"
print(demo())
4.2. 静态方法和类方法实现(了解)
e.g. 静态方法实现
class Test:
def __init__(self, func):
self.func = func
@staticmethod
def set_func(func):
def call_func(*args, **kwargs):
print("装饰器功能")
return func(*args, **kwargs)
return call_func
@Test.set_func # demo = Test.set_func(demo)
def demo():
return "haha"
print(demo())
e.g. 类方法实现
class Test:
def __init__(self, func):
self.func = func
@classmethod
def set_func(cls, func):
def call_func(*args, **kwargs):
print("装饰器功能")
return func(*args, **kwargs)
return call_func
@Test.set_func # demo = Test.set_func(demo)
def demo():
return "haha"
print(demo())
5. 带参数的装饰器
-
使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回是装饰器 ,因为 @ 符号需要配合装饰器实例使用
-
装饰器只能接收一个参数,并且还是函数类型。
5.1. 代码示例
ddef set_params(level):
def set_func(func):
def call_func(*args, **kwargs):
if level == 1:
print("---权限级别 1---")
elif level == 2:
print("---权限级别 2---")
return func(*args, **kwargs)
return call_func
return set_func # 返回一个装饰器
@set_params(1) # 先执行函数set_params保存参数,然后使用函数返回的set_func装饰器对demo1进行装饰
def demo1():
print("---demo1---")
return "ok"
@set_params(2) # 先执行函数set_params保存参数,然后使用函数返回的set_func装饰器对demo2进行装饰
def demo2():
print("---demo1---")
return "ok"
demo1()
demo2()
5.2. 错误写法
def set_func(func, level):
def call_func(*args, **kwargs):
if level == 1:
print("---权限级别 1---")
elif level == 2:
print("---权限级别 2---")
return func(*args, **kwargs)
return call_func
@set_func(1) # 先执行函数set_params保存参数,然后使用函数返回的set_func装饰器对demo进行装饰
def demo1():
print("---demo1---")
return "ok"
@set_func(2) # 先执行函数set_params保存参数,然后使用函数返回的set_func装饰器对demo进行装饰
def demo2():
print("---demo1---")
return "ok"
demo1()
demo2()
运行结果:
提示装饰器缺失参数,因为装饰器只能接收一个函数的引用作为参数。
Traceback (most recent call last):
File "d:\Code\python_learn_code\video_learn_code\temp2.py", line 11, in <module>
@set_func(1) # 先执行函数set_params保存参数,然后使用函数返回的set_func装饰器对demo进行装饰
TypeError: set_func() missing 1 required positional argument: 'level'
6. 装饰器使用简单示例
6.1. 计算函数执行时间
import time
def set_func(func):
def call_func(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
end_time = time.time()
print(f"函数的执行时间是: {end_time - start_time}")
return ret
return call_func
@set_func
def demo():
for i in range(2):
print(i)
time.sleep(1)
demo()
6.2. 添加日志、权限校验功能
def set_func(func):
def call_func(*args, **kwargs):
print("日志、权限校验功能")
return func(*args, **kwargs)
return call_func
@set_func
def demo():
print("xxx")
return "ok"
demo()
6.3. html内容添加标签
def set_func(func):
def call_func(*args, **kwargs):
return f"<h1>{func(*args, **kwargs)}</h1>"
return call_func
@set_func
def demo(body):
return body
print(demo("xxxx"))
本文来自博客园,作者:NFTO,转载请注明原文链接:https://www.cnblogs.com/NFTO21/p/15144331.html