|
| 1 | +<!-- |
| 2 | + 作者:华校专 |
| 3 | + |
| 4 | +** 本文档可用于个人学习目的,不得用于商业目的 ** |
| 5 | +--> |
| 6 | +# 装饰器 |
| 7 | +1.装饰器是用于包装其他可调用对象的一个可调用对象,<font color='red'>它是一个可调用对象,其调用参数为另一个可调用对象<,它返回一个可调用对象</font> |
| 8 | + |
| 9 | +* 一个函数对象是可调用对象。 |
| 10 | +* 一个类对象是可调用对象,对它调用的结果就是返回类的实例 |
| 11 | +* 实现了`.__call__()`方法的类,其实例对象是可调用对象,对它调用的结果就是调用`.__call__()`方法 |
| 12 | + |
| 13 | +  |
| 14 | + |
| 15 | +装饰器有两种使用形式: |
| 16 | + |
| 17 | +* 函数的装饰器:在函数对象定义的时候使用装饰器,用于管理该函数对象 |
| 18 | +* 类的装饰器:在类定义的时候使用该装饰器,用于管理该类以及类的实例 |
| 19 | + |
| 20 | +>装饰器是装饰器模式的一个实现 |
| 21 | +
|
| 22 | +2.函数的装饰器:用于管理函数。函数的装饰器声明为: |
| 23 | + |
| 24 | +``` |
| 25 | +@decorator |
| 26 | +def func(*pargs,**kwargs): |
| 27 | + pass |
| 28 | +``` |
| 29 | +即在正常的函数定义之前冠以`@decorator`说明符(即装饰器声明)。它等价于: |
| 30 | + |
| 31 | +``` |
| 32 | +def func(*pargs,**kwargs): |
| 33 | + pass |
| 34 | +func=decorator(func) |
| 35 | +``` |
| 36 | + |
| 37 | +* 类中的`@staticmethod`、`@classmethod`、`@property`均为装饰器 |
| 38 | +* 执行了装饰器的`def`之后,函数名指向的不再是原来的函数对象,而是: |
| 39 | + * 一个可调用对象, 当`decorator`是个函数时由`decorator(func)`函数返回的 |
| 40 | + * `decorator`类的实例,当`decorator`是个类时,由`decorator(func)`构造方法返回 |
| 41 | + |
| 42 | +  |
| 43 | + |
| 44 | +3.类的装饰器:用于管理类。类的装饰器声明为: |
| 45 | + |
| 46 | +``` |
| 47 | +@decorator |
| 48 | +class A: |
| 49 | + pass |
| 50 | +``` |
| 51 | +即在正常的类定义之前冠以`@decorator`说明符(即装饰器声明)。它等价于: |
| 52 | + |
| 53 | +``` |
| 54 | +class A: |
| 55 | + pass |
| 56 | +A=decorator(A) |
| 57 | +``` |
| 58 | + |
| 59 | +* 类的装饰器并不是拦截创建实例的函数调用,而是返回一个不同的可调用对象 |
| 60 | +* 执行了装饰器的`class`之后,类名指向的不再是原来的类对象,而是: |
| 61 | + * 一个可调用对象, 当`decorator`是个函数时由`decorator(func)`函数返回的 |
| 62 | + * `decorator`类的实例,当`decorator`是个类时,由`decorator(func)`构造方法返回 |
| 63 | + |
| 64 | +  |
| 65 | + |
| 66 | +3.装饰器只是一个返回可调用对象的可调用对象,它没有什么特殊的地方。 |
| 67 | + |
| 68 | +* 可以用函数实现装饰器: |
| 69 | + |
| 70 | +``` |
| 71 | +def decorator(func): #定义了一个叫decorator的装饰器 |
| 72 | + #某些处理 |
| 73 | + return func #返回可调用对象 |
| 74 | +``` |
| 75 | + |
| 76 | +* 也可以用类来实现装饰器: |
| 77 | + |
| 78 | +``` |
| 79 | +class decorator: |
| 80 | + def __init__(self,func): |
| 81 | + self.func=func |
| 82 | + def __call__(self,*args,**kwargs): |
| 83 | + return self.func |
| 84 | +``` |
| 85 | + |
| 86 | +* 通常用嵌套类来实现装饰器: |
| 87 | + |
| 88 | +``` |
| 89 | +def decorator(func): #定义了一个叫decorator的装饰器 |
| 90 | + def wrapper(*args): |
| 91 | + #使用func或其他的一些工作 |
| 92 | + return wrapper #返回可调用对象 |
| 93 | +``` |
| 94 | + |
| 95 | +  |
| 96 | + |
| 97 | +4.装饰器的嵌套: |
| 98 | + |
| 99 | +* 函数的装饰器的嵌套: |
| 100 | + |
| 101 | +``` |
| 102 | +@decoratorA |
| 103 | +@decoratorB |
| 104 | +@decoratorC |
| 105 | +def func(): |
| 106 | + pass |
| 107 | +``` |
| 108 | +等价于 |
| 109 | + |
| 110 | +``` |
| 111 | +def f(): |
| 112 | + pass |
| 113 | +f=A(B(C(f))) |
| 114 | +``` |
| 115 | +* 类的装饰器的嵌套: |
| 116 | + |
| 117 | +``` |
| 118 | +@decoratorA |
| 119 | +@decoratorB |
| 120 | +@decoratorC |
| 121 | +class M: |
| 122 | + pass |
| 123 | +``` |
| 124 | +等价于 |
| 125 | + |
| 126 | +``` |
| 127 | +class M: |
| 128 | + pass |
| 129 | +M=A(B(C(M))) |
| 130 | +``` |
| 131 | +>每个装饰器处理前一个装饰器返回的结果,并返回一个可调用对象 |
| 132 | +
|
| 133 | +5.装饰器可以携带参数。 |
| 134 | + |
| 135 | +* 函数定义的装饰器带参数:它其实是一个嵌套函数。 |
| 136 | + * 外层函数的参数为装饰器参数,返回一个函数(内层函数) |
| 137 | + * 内层函数的参数为`func`,返回一个可调用参数,<font color='red'>内层函数才是真正的装饰器</font> |
| 138 | + |
| 139 | +``` |
| 140 | +def decorator(*args,**kwargs): |
| 141 | + print("this is decorator1:",args,kwargs) |
| 142 | + def actualDecorator(func): # 这才是真实的装饰器 |
| 143 | + ... |
| 144 | + return func |
| 145 | + return actualDecorator |
| 146 | +``` |
| 147 | + |
| 148 | +  |
| 149 | + |
| 150 | +* 类定义的装饰器带参数:它其实是一个嵌套类。 |
| 151 | + * 外层类的初始化函数的参数为装饰器参数,外层类的`__call__`函数的参数为`func`,返回值为一个类的实例(内部类实例) |
| 152 | + * 内层类的初始化函数参数为`func`;内层类的`__call__`函数使用`func`,<font color='red'>内层类才是真正的装饰器</font> |
| 153 | + |
| 154 | +``` |
| 155 | +class decorator2: |
| 156 | + class ActualDecorator: #这才是真实的装饰器 |
| 157 | + def __init__(self,func): |
| 158 | + ... |
| 159 | + self.func=func#记住func |
| 160 | + def __call__(self,*args,**kwargs): |
| 161 | + ... |
| 162 | + return self.func(*args,**kwargs) #使用func |
| 163 | + def __init__(self,*args,**kwargs): |
| 164 | + ... |
| 165 | + def __call__(self,func): |
| 166 | + ... |
| 167 | + return decorator2.ActualDecorator(func) |
| 168 | +``` |
| 169 | + |
| 170 | +  |
| 171 | + |
| 172 | +总结: |
| 173 | + |
| 174 | +* 不带参数的装饰器`decorator`装饰一个名字`F`(可能为函数名、也可能为类名)`@decorator`:则执行的是:`F=decorator(F)`,直接使用`F` |
| 175 | +* 带参数的装饰器`decorator`装饰一个名字`F`(可能为函数名、也可能为类名)`@decorator(args)`:则执行的是:`F=decorator(args)(F)`,间接使用`F` |
| 176 | + |
| 177 | +6.利用装饰器可以实现单例模式: |
| 178 | + |
| 179 | +``` |
| 180 | +def Singleton(cls): |
| 181 | + instance=None |
| 182 | + def onCall(*args,**kwargs): |
| 183 | + nonlocal instance |
| 184 | + if instance == None: |
| 185 | + instance=cls(*args,**kwargs) |
| 186 | + return instance |
| 187 | + return onCall |
| 188 | +@Singleton |
| 189 | +class A: |
| 190 | + pass |
| 191 | +``` |
| 192 | +  |
| 193 | + |
| 194 | +7.利用装饰器可以跟踪对象的调用接口,从而管理对实例的接口访问(如统计调用次数,打印调用日志) |
| 195 | + |
| 196 | +``` |
| 197 | +def Tracer(cls): |
| 198 | + class Wrapper: |
| 199 | + def __init__(self,*args,**kwargs): |
| 200 | + self.wrapped=cls(*args,**kwargs) |
| 201 | + def __getattr__(self,name): |
| 202 | + print('Trace:'+name) |
| 203 | + return getattr(self.wrapped,name) |
| 204 | + return Wrapper |
| 205 | +@Tracer |
| 206 | +class A: |
| 207 | + pass |
| 208 | +``` |
| 209 | + |
| 210 | +  |
| 211 | + |
| 212 | +8.装饰器也可以直接管理函数和类,而不仅仅只是管理对他们的调用 |
| 213 | + |
| 214 | +* 利用装饰器添加函数和类到注册表: |
| 215 | + |
| 216 | +``` |
| 217 | +register_dict={} |
| 218 | +def register(obj): |
| 219 | + register_dict[obj.__name__]=obj |
| 220 | + return obj |
| 221 | +@register |
| 222 | +def func(): |
| 223 | + pass |
| 224 | +``` |
| 225 | + |
| 226 | +  |
| 227 | + |
| 228 | +* 利用装饰器为函数和类添加属性 |
| 229 | + |
| 230 | +``` |
| 231 | +def register(obj): |
| 232 | + obj.label=0 |
| 233 | + return obj |
| 234 | +@register |
| 235 | +def func(): |
| 236 | + pass |
| 237 | +``` |
| 238 | + |
| 239 | +  |
| 240 | + |
| 241 | + |
| 242 | + |
| 243 | + |
| 244 | + |
| 245 | + |
| 246 | + |
| 247 | + |
| 248 | + |
0 commit comments