* 装饰器1:函数装饰器 <https://www.cnblogs.com/f-ck-need-u/p/10198247.html>
* 装饰器2:类装饰器 <https://www.cnblogs.com/f-ck-need-u/p/10205168.html>
* 装饰器3:进阶 <https://www.cnblogs.com/f-ck-need-u/p/10204617.html>
本文是装饰器相关内容的第二篇,关于类装饰器。


"类装饰器"有两种解读方式:用来装饰类的装饰器;类作为装饰器装饰其它东西。你如何认为取决于你,两种说法都有出现在其它的文章中。我的文章中是将"类装饰器"解读为第一种方式,即装饰类的东西。而“类作为装饰器装饰其它东西”,我都会为其标注"类作为装饰器"或"作为装饰器的类"以避免歧义。

类装饰器的形式

函数装饰器是装饰函数(方法)的,类装饰器是装饰类的,它们的表现形式是一样的。
@decorator class cls: ... c = cls()
等价于:
class cls: ... cls = decorator(cls) c = cls()
它的效果是创建实例对象的时候,会触发装饰器中的代码逻辑。

再细细一想,发现decorator(cls)要返回的是一个类,所以decorator中的结构大概是这样的:
def decorator(cls): class wrapper: ... return wrapper
这样就会让被包装的类cls实际变成wrapper类
,并且以后调用cls构造对象的时候,实际上是调用wrapper类来构造对象。换句话说,wrapper已经拦截了对所有的cls操作。

但并非一定如此,比如直接返回原始的cls:
def decorator(cls): ...do something about cls... return cls
这种方式比较简单,本文主要对前一种方式进行详细解释。

由于返回的是class wrapper,那么它装饰类的时候,假设所装饰的类有构造方法__init__,构造方法中有属性,这个类中还有方法。如下:
@decorator class cls(): # 等价于cls = decorator(cls) def __init__(self, x, y):
self.attrx = x self.attry = y def method(self): return self.x, self.y
那么在包装器wrapper中,需要能够构造出这个对象,并且能够取得被包装类的对象属性、类属性。如下:
def decorator(cls): class wrapper(): def __init__(self, *args, **kwargs):
self.wrapped = cls(*args, **kwargs) def __getattr__(self, name): return
getattr(self.wrapped, name) return wrapper
因为操作cls类的时候,实际上是在操作wrapper类。所以构造cls对象的时候:
c = cls(3, 4)
实际上是在调用wrapper(3, 4)来构造对象,所以会执行wrapper里的__init__
。但类装饰器最终的目标是为了扩展类cls,所以在wrapper里必须得构造出cls的对象。上面采取的方式是通过cls()来构造cls对象,并放在wrapper对象的一个属性wrapped中。

因为cls已经被金蚕脱壳成了wrapper,所以要获取到cls的属性必须在wrapper中重写属性获取的方式。

下面是一个示例:
def decorator(cls): class wrapper(): def __init__(self, *args, **kwargs):
self.wrapped = cls(*args, **kwargs) def __getattr__(self, name): return
getattr(self.wrapped, name) return wrapper @decorator class cls(): def
__init__(self, x, y): self.attrx = x self.attry = y def method(self): return
self.attrx, self.attry c = cls(3, 4) print(c.attrx) print(c.attry)
print(c.method())
输出结果:
3 4 (3, 4)

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信