描述符和类的装饰器(三十九)
简单实现
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方法,它由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了