概述
装饰器本质就是一个语法糖@,执行固定的操作:用decorator(obj)
的返回值替换obj
这个名字,即obj = decorator(obj)
。扩展功能是通过python的其他特性(如闭包)实现的。
规则
- 被装饰者
obj
只能是函数或者类 - 只要
decorator
是一个可调用对象就行,因此函数和类都可以作为装饰器 -
decorator
返回值理论上没有限制,但通常都会调用被装饰对象,因此返回值一般也是可调用对象
流程
-
调用装饰器,参数为被装饰者:
decorator(obj)
,执行装饰器内部代码 -
返回装饰后的对象,替换被装饰者,使
obj
指向装饰器返回的对象
分别以装饰器和被装饰者的视角来看:
- 装饰器
- 函数:正常调用函数,返回一般也是函数
- 类:调用类的
__init__
方法,返回一个实例对象;实现类的__call__
方法,使其成为可调用对象
- 被装饰者
-
普通函数:装饰器返回的对象只要可调用就行
-
类:装饰器返回的对象只要可调用就行,调用的返回值理论上没有限制,但通常是一个扩展后的类的实例对象
-
实例函数(成员函数?):装饰器必须返回函数;如果返回对象,那在实例化时,不会变成
bound method
(绑定方法?),也就无法传self进去def decorator(obj): # 用函数来定义装饰器 def warpper(*args, **kwargs): # (*args, **kwargs) 保持参数一致的最简单的方法 return obj(*args, **kwargs) return warpper class ClassDecorator: # 用类来定义装饰器 def __init__(self, obj) -> None: self.obj = obj def __call__(self, *args, **kwargs): return self.obj(*args, **kwargs) class A(object): @decorator # 把对象的__call__方法包装成函数,就能变成bound method了 @ClassDecorator # 返回对象,不能变成bound method def instance_method(self): print('call instance_fun') @ClassDecorator # 返回对象,不能变成bound method def instance_obj(self): print('call instance_fun') a = A() print(a.instance_method) # <bound method decorator.<locals>.warpper of <__main__.A object at 0x7fc85fcabbb0>> print(a.instance_obj) # <__main__.ClassDecorator object at 0x7fc85fd26b20>
-
用途
- 不修改函数调用方式的前提下,在函数的前后添加功能(如:计算函数执行时间)reference(内容有错误)
- 注册器类,往对象中注册函数,通过字符串调用 reference
- 单例类/单例模式:仅有一个实例对象的类
装饰器代码的执行时机
在被装饰对象定义或初始化完成后,就会进行装饰(执行装饰器),而不是等到调用被装饰对象时才进行装饰
def decorator(obj): # 用函数来定义装饰器
name = hasattr(obj, '__qualname__') and obj.__qualname__ or obj
print(f'call decorator, obj is {name}')
def warpper(*args, **kwargs): # (*args, **kwargs) 保持参数一致的最简单的方法
print('call decorator->warpper')
return obj(*args, **kwargs)
return warpper
class ClassDecorator: # 用类来定义装饰器
def __init__(self, obj) -> None:
name = hasattr(obj, '__qualname__') and obj.__qualname__ or obj
print(f'call ClassDecorator, obj is {name}')
self.obj = obj
def __call__(self, *args, **kwargs):
print('call ClassDecorator->__call__')
return self.obj(*args, **kwargs)
# 定义 my_fun1 然后执行 decorator(my_fun1)
@decorator
def my_fun1(): # 用于装饰函数 my_fun1 = decorator(my_fun1) 将 my_fun1 替换为包装后的函数
print('call my_fun1')
# 定义 my_fun2 然后执行 ClassDecorator(my_fun2)
@ClassDecorator
def my_fun2():
print('call my_fun2')
# 初始化 A 然后执行 decorator(A)
@decorator
class A(object): # 用于装饰类 A = decorator(A)
print('init class A begin...')
def __init__(self) -> None:
print('call A.__init__')
@decorator # 返回函数,可以变成bound method
def instance_fun1(self):
print('call instance_fun1')
@decorator # 把对象的__call__方法包装成函数,就能变成bound method了
@ClassDecorator # 返回对象,不能变成bound method
def instance_fun2(self):
print('call instance_fun2')
print('init class A end...')
print('before call my_fun1')
my_fun1()
print('before call my_fun2')
my_fun2()
a = A()
a.instance_fun1()
运行结果如下:
call decorator, obj is my_fun1
call ClassDecorator, obj is my_fun2
init class A begin...
call decorator, obj is A.instance_fun1
call ClassDecorator, obj is A.instance_fun2
call decorator, obj is <__main__.ClassDecorator object at 0x7fc79d1bf850>
init class A end...
call decorator, obj is A
before call my_fun1
call decorator->warpper
call my_fun1
before call my_fun2
call ClassDecorator->__call__
call my_fun2
call decorator->warpper
call A.__init__
call decorator->warpper
call instance_fun1
总结
调用被装饰者obj
时,可以把obj
看成是decorator
的返回值