• python装饰器2:类装饰器


    本文是装饰器相关内容的第二篇,关于类装饰器。

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

    类装饰器的形式

    函数装饰器是装饰函数(方法)的,类装饰器是装饰类的,它们的表现形式是一样的。

    @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)
    
  • 相关阅读:
    附近有什么?8款可以查周边的App
    实体店里充话费要怎么弄
    怎样买手机号?
    手机号是SIM卡的号呢,还是买手机时就带的
    网站SSL证书在线检测
    未来什么行业最赚钱
    陈安之-如何选择最赚钱的行业
    斗鱼宣布获C轮15亿融资 直播行业进入资本时代
    2016年Godaddy最新域名转出教程
    GoDaddy账户间域名转移PUSH以及ACCEPT接受域名过户方法
  • 原文地址:https://www.cnblogs.com/f-ck-need-u/p/10205168.html
Copyright © 2020-2023  润新知