装饰器的本质
装饰器(不带参数的装饰器)从实现上看,就是一个“输入和输出都是函数对象”的函数;
带参数的装饰器实际上就是一个返回普通装饰器的函数(可以接受任意参数);
装饰器语法实际上就是以下两种调用形式的语法糖:
# 不带参数 func = decorator(func) # 带参数 func = decorator(*args)(func)类装饰器同理,就是一个“输入和输出都是类对象”的函数,也是类似的语法糖;
# 不带参数 clas = decorator(clas) # 带参数 clas = decorator(*args)(clas)装饰器类:具有装饰器功能的类,通过重载
__call__实现;
示例 1:装饰器(不带参数)
示例 1:装饰器(不带参数)定义装饰器
def dn(fn): # 输入是一个函数 """一个简单的装饰器,打印被装饰函数的结果""" def wrapper(*args, **kwargs): ret = fn(*args, **kwargs) print(f'Result: {ret}') return ret # 返回原函数的结果 return wrapper # 返回是一个函数调用装饰器
@dn def func(a, b): return a + b ret = func(1, 2) # Result: 3 print(ret) # 3等价行为
def func(a, b): return a + b func = dn(func) ret = func(1, 2) # Result: 3 print(ret) # 3
示例 2:带参数的装饰器
示例 2:带参数的装饰器定义装饰器
def dn_pro(prefix): # 普通函数,可以传入任意参数 """自定义打印前缀""" def dn(fn): """一个简单的装饰器,打印被装饰函数的结果""" def wrapper(*args, **kwargs): ret = fn(*args, **kwargs) print(f'{prefix}: {ret}') return ret # 返回原函数的结果 return wrapper return dn # 返回一个装饰器调用装饰器
@dn_pro('ret') def func(a, b): """""" return a + b等价行为
def func(a, b): return a + b func = dn_pro('ret')(func) ret = func(1, 2) # ret: 3 print(ret) # 3
示例 3:functools.wraps 的作用
示例 3:functools.wraps 的作用在示例 2 中,打印
func.__name__的结果是wrapper; 这是因为经过装饰器后,func实际上已经指向了另一个函数;这时可以通过
functools.wraps装饰器保留原函数的相关属性,用法如下:functools.wraps本身也是一个带参数的装饰器;
import functools # 导入 functools
def dn_pro(prefix): # 普通函数,可以传入任意参数
"""自定义打印前缀"""
def dn(fn):
"""一个简单的装饰器,打印被装饰函数的结果"""
@functools.wraps(fn) # 保留 fn 的相关属性
def wrapper(*args, **kwargs):
ret = fn(*args, **kwargs)
print(f'{prefix}: {ret}')
return ret # 返回原函数的结果
return wrapper
return dn # 返回一个装饰器
@dn_pro('ret')
def func(a, b):
return a + b
print(func.__name__) # func示例 4:类装饰器
示例 4:类装饰器类装饰器就是一个“输入和输出都是类对象”的函数;
定义一个类装饰器:给装饰的类添加一个
printf函数def dn(cls): def printf(self): print(self.__dict__) # cls.printf = printf setattr(cls, 'printf', printf) return cls调用装饰器
@dn class A: def __init__(self, a, b): self.a = a self.b = b a = A(1, 2) a.printf() # {'a': 1, 'b': 2}等价行为
class A: def __init__(self, a, b): self.a = a self.b = b A = dn(A) a = A(1, 2) a.printf() # {'a': 1, 'b': 2}
示例 5:装饰器类(不带参数)
示例 5:装饰器类(不带参数)装饰器类:具有装饰器功能的类,通过重载
__call__实现;需要注意的是,该场景下不能使用
functools.wraps,而应该使用functools.update_wrapper,详见示例;
定义装饰器类
class Dn: def __init__(self, fn): functools.update_wrapper(self, fn) # ! self.fn = fn def __call__(self, *args, **kwargs): ret = self.fn(*args, **kwargs) print(f'Result:', ret) return ret调用装饰器类
@Dn def func(a, b): return a + b ret = func(1, 2) # Result: 3 print(ret) # 3 print(func.__name__) # func等价行为
def func(a, b): return a + b func = Dn(func) ret = func(1, 2) # Result: 3 print(ret) # 3 print(func.__name__) # func
示例 6:带参数的装饰器类
示例 6:带参数的装饰器类定义装饰器类
class Dn: def __init__(self, prefix): self.prefix = prefix def __call__(self, fn): @functools.wraps(fn) def wrapper(*args, **kwargs): ret = fn(*args, **kwargs) print(f'{self.prefix}:', ret) return ret return wrapper调用装饰器类
@Dn('ret') def func(a, b): return a + b ret = func(1, 2) # ret: 3 print(ret) # 3 print(func.__name__) # func等价行为
def func(a, b): return a + b func = Dn('ret')(func) ret = func(1, 2) # ret: 3 print(ret) # 3 print(func.__name__) # func
Last updated