描述符和类的装饰器(三十九)

简单实现

def deco(obj):
    print("---->装饰器")
    obj.x = 1
    obj.y = 2
    obj.z = 3
    return obj

@deco
class Foo:
    pass

f = Foo()
print(Foo.__dict__)
'''
---->装饰器
{'__module__': '__main__', 
'z': 3, 'y': 2, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, 
'x': 1, '__dict__': <attribute '__dict__' of 'Foo' objects>}
'''

加强版

def Typed(**kwargs):
    def deco(obj):
        for key, value in kwargs.items():
            setattr(obj, key, value)
        return obj
    return deco

@Typed(x=1, y=2, z=3)
class Foo:
    pass

@Typed(name="zhangsan")
class People:
    pass

应用:

class Typed:
    def __init__(self,key, expected_type):
        self.key = key
        self.expected_type = expected_type
    def __get__(self, instance, owner):
        return instance.__dict__[self.key]
    def __set__(self, instance, value):
        if not isinstance(value, self.expected_type):
            raise TypeError("%s is not %s" %(value, self.expected_type))
        instance.__dict__[self.key] = value
    def __delete__(self, instance):
        instance.__dict__.pop(self.key)

def deco(**kwargs):
    def wrapper(obj):
        for key, value in kwargs.items():
            setattr(obj, key, Typed(key, value))
        return obj
    return wrapper

@deco(name=str, age=int, gender=str) # 加括号就要运行,结果wrapper = wrapper(People)
class People:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

p1 = People('zhangsan', 18, 'male')
print(People.__dict__)

描述符总结

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.

class Lazyproperty:
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, owner):
        return self.func(instance)

class Room:
    def __init__(self, w, h):
        self.width=w
        self.height=h

    @Lazyproperty
    def cal_area(self):
        return self.width * self.height
r = Room(4, 5)
print(r.cal_area)
class Lazyproperty:
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, owner):
        print('get...')
        if instance is None:
            return self
        res=self.func(instance)
        setattr(instance, self.func.__name__, res)
        return res

class Room:
    def __init__(self, w, h):
        self.width=w
        self.height=h

    @Lazyproperty
    def cal_area(self):
        return self.width * self.height
r = Room(4, 5)
print(r.__dict__) # {'height': 5, 'width': 4}
print(r.cal_area) # #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法
print(r.__dict__) # {'cal_area': 20, 'height': 5, 'width': 4}
print(r.cal_area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算
print(r.cal_area)
'''
get...
20
20
20
# 只触发了一次__get__方法,后面直接从对象的字典属性调用
'''
#缓存不起来了

class Lazyproperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        else:
            value=self.func(instance)
            instance.__dict__[self.func.__name__]=value
            return value
        # return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情
    def __set__(self, instance, value):
        print('hahahahahah')

class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
    def area(self):
        return self.width * self.length

print(Room.__dict__)
r1=Room('alex',1,1)
print(r1.area)
print(r1.area) 
print(r1.area) 
print(r1.area) #缓存功能失效,每次都去找描述符了,为何,因为描述符实现了set方法,它由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了
原文地址:https://www.cnblogs.com/xiangtingshen/p/10466630.html