Python开发之路 - 描述符的应用
1.描述符的应用
用描述符实行类型检测
class Dtype: def __init__(self,key,expect_type): self.key = key self.expect_type = expect_type def __get__(self, instance, owner): return instance.__dict__[self.key] def __set__(self, instance, value): if not isinstance(value,self.expect_type): raise TypeError('%s传入的类型有误,应该为%s'%(self.key,self.expect_type)) instance.__dict__[self.key] = value def __delete__(self, instance): instance.__dict__.pop[self.key] class People: name = Dtype('name',str) age = Dtype('age',int) def __init__(self,name,age): self.name = name self.age = age p1 = People('chris','18') print(p1.__dict__)
2.类的装饰器
def daju(**kwargs): def deco(obj): for k,v in kwargs.items(): setattr(obj,k,v) return obj return deco @daju(x=1,y=2) #先运行daju(x=1,y=2) 返回一个deco 相当于@deco Foo=deco(Foo) class Foo: pass print(Foo.__dict__)
此时实现了 对类的修饰 加入了x,y的属性
加入对类型的限制:
class Typed: def __init__(self,key,expect_type): self.key = key self.expect_type = expect_type def __set__(self, instance, value): if not isinstance(value,self.expect_type): raise TypeError('%s输入的类型有误,应为%s类型' %(self.key,self.expect_type)) instance.__dict__[self.key] = value def deco(**kwargs): #kwargs 接收到 {'name' = str, 'age' = int, 'salary' = float} def wrapper(obj): for k,v in kwargs.items(): print(k,v) setattr(obj,k,Typed(k,v)) #设置值时,将K给描述符代理 实现类型限制 既name = Typed('name',str) #setattr(Foo,'name',Typed('name',str)) 上面相当于执行这个 Foo.name = Typed('name',str) return obj return wrapper @deco(name = str, age = int, salary = float) #装饰器代替了三个重复的代码 实现给Foo类设定属性 只不过这个属性是描述符 对类的类型实现限制 class Foo: # name = Typed('name',str) # age = Typed('age',int) # salary = Typed('salary',str) def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary p1 = Foo('chris',19,25.7) print(Foo.__dict__) print(p1.__dict__)
3.利用描述符自定制property
class Lazyproperty: def __init__(self,func): self.func = func # print('22222') def __get__(self, instance, owner): #实例调用时instance是实例自己 owner 为其所在的类 类调用时instance 为 None owner为类自己 if instance is None: return self val = self.func(instance) return val class Room: def __init__(self,name,length,width): self.name = name self.length = length self.width = width @Lazyproperty # area = Lazyproperpy(area) 也是在增加描述符 def area(self): return self.length * self.width r1 = Room('厕所',18,23) print(Room.area)
print(r1.area)
print(r1.area)
print(r1.area)
!!!此时实例调用area方法时会触发get方法,get方法中会自动将area算出来的值保存在实例的字典中,又因为Lazyproperty为非数据描述符,优先级低于实例属性,所以下次调用area方法时,会优先从实例属性字典中查找,所以不会触发get方法
4.自定制property功能实现延迟计算功能:定义set方法使描述符成为数据描述符,因此每次调用时都会从数据描述符中调用get方法
import time class Lazyproperty: def __init__(self,func): self.func = func # print('22222') def __get__(self, instance, owner): #实例调用时instance是实例自己 owner 为其所在的类 类调用时instance 为 None owner为类自己 time.sleep(2) print('get方法') if instance is None: return self val = self.func(instance) setattr(instance,self.func.__name__,val) #将area算出来的结果存放到实例自己的属性名为area的字典当中 return val def __set__(self, instance, value): pass class Room: def __init__(self,name,length,width): self.name = name self.length = length self.width = width @Lazyproperty # area = Lazyproperpy(area) 也是在增加描述符 def area(self): return self.length * self.width r1 = Room('厕所',18,23) print(r1.area) print(r1.area) print(r1.area)
5.利用描述符自定制一个classmethod
class ClassMethod: def __init__(self,func): self.func=func def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身, def feedback(*args,**kwargs): print('在这里可以加功能啊...') return self.func(owner,*args,**kwargs) return feedback class People: name='linhaifeng' @ClassMethod # say_hi=ClassMethod(say_hi) def say_hi(cls,msg): print('你好啊,帅哥 %s %s' %(cls.name,msg)) People.say_hi('你是那偷心的贼') p1=People() p1.say_hi('你是那偷心的贼')
6.property补充 为静态属性设置值时用的。setter方法 删除静态属性值时 用.deleter方法
class Foo: @property def AAA(self): print('get的时候运行我啊') @AAA.setter def AAA(self,value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA
或者
lass Foo:
def get_AAA(self):
print('get的时候运行我啊')
def set_AAA(self,value):
print('set的时候运行我啊')
def delete_AAA(self):
print('delete的时候运行我啊')
AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
应用场景如下:
class Goods: def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价
7.元类
元类是类的类,是类的模板,元类是用来控制如何创建类的,元类的实例是类,正如类的实例是对象
type是pyhton的一个内建元类,用来直接控制生成类
FFo = type('Foo',(object,),{'x':1}) # 用type创建类 第一个参数为类名,第二个参数为继承的类,新式类默认加个object,第三个参数是属性
模拟元类帮你生成对象的过程:可以自己控制实例化的过程 定制自己的逻辑
class Mytype(type): def __init__(self,a,b,c): print('这是我的元类') def __call__(self, *args, **kwargs): obj = object.__new__(self) ##产生一个Foo的对象 self就是Foo self.__init__(obj,*args,**kwargs) #为Foo生成的对象的字典里加属性 return obj class Foo(metaclass=Mytype): #相当于MYtype('Foo',(object),{}) 传过去四个参数 def __init__(self,name): self.name = name f1 = Foo('chris') #相当于在执行Mytype的call方法