描述符应用与类的装饰器
1.上下文管理协议
with obj as f:
"代码块"
1.with obj ----> 触发obj.__enter__(),拿到返回值
2.返回值赋值f,则f = 返回值
3.执行代码块
3.1.没有异常情况,整个代码块运行完毕后触发__exit__,它的三个参数都不起作用
3.2.有异常情况,从异常出现位置直接触发__exit__
3.2.1如果__exit__的返回值为True,代表吞掉异常,正常执行with函数外的语句
3.2.2如果__exit__的返回值不为True,代表吐出了异常,代码终止
3.2.3exit的运行完毕就代表了整个with语句的执行完毕
__enter__()方法和__exit__()方法的执行
class Open: def __init__(self,name): self.name = name def __enter__(self): print("执行enter") return self def __exit__(self, exc_type, exc_val, exc_tb): print("执行exit") with Open("a.txt") as f: #with Open("a.txt") as f, 触发__enter__(self) 方法,返回self,并赋值给f print(f) print(f.name) #结束文件时,执行__exit__操作
return True的打开与关闭会呈现两种状态,对应上面3.2.1和3.2.2
class Open: def __init__(self,name): self.name = name def __enter__(self): print("执行enter") return self def __exit__(self, exc_type, exc_val, exc_tb): print("执行exit") print(exc_type) print(exc_val) print(exc_tb) return True with Open("a.txt") as f: #with Open("a.txt") as f, 触发__enter__(self) 方法,返回self,并赋值给f print(f) print(f.name) #结束文件时,执行__exit__操作 print("=========================>") print("=========================>") print("=========================>") print("=========================>") print("=========================>") print(asdfsdfsdfsssssdsdddddddddddddddddddd) #本行代码出现异常,跳过with..as..中代码,直接执行with..as..外代码 print("=========================>") print("=========================>") print("=========================>") print("=========================>") print("=========================>") print("00000000000000000000000000000000000")
2.描述符应用
class str: def __get__(self,instance,owner): print("str调用") def __set__(self,instance,value): print("str设置...") def __delete__(self,instance): print("str删除") #描述符int class Int: def __get__(self,instance,owner): print("Int调用") def __set__(self,instance,value): print("Int设置...") def __delete__(self,instance): print("Int删除") class Typed: def __get__(self,instance,owner): print('get方法') print("instance参数【%s】",instance) print("owner参数【%s】"%owner) def __set__(self, instance, value): print("set方法") print("instance参数【%s】" %instance) #instance参数为p1对象地址 print("value参数【%s】" %value) def __delete__(self,instance): print("delete方法") print("instance参数【%s】"%instance) class People: # name = str() # age = Int() name = Typed() #name被数据描述符(类属性)代理 def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary p1 = People("alex",18,18.8) print(p1) p1.name p1.name = "egon" print(p1.__dict__)
描述符字符检测(一个变量):
class Typed: def __init__(self,key): self.key = key # self.expected_type = expected_type def __get__(self,instance,owner): print('get方法') # print("instance参数【%s】",instance) # print("owner参数【%s】"%owner) return instance.__dict__[self.key] def __set__(self, instance, value): #描述符 123-->name-->value--底层字典instance.__dict__[self.key] print(value) print("set方法") if not isinstance(value,str):#比较字符串类型 print("传入类型有误") return # raise TypeError("传入类型有误") instance.__dict__[self.key] = value def __delete__(self,instance): print("delete方法") # print("instance参数【%s】"%instance) instance.__dict__.pop(self.key) class People: # name = str() # age = Int() name = Typed("name") #name被数据描述符(类属性)代理,所以不直接传入instance底层字典,age和salary未被数据描述符占用,age,salary作为实例属性传入底层字典 def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary p1 = People("jack",25,25.5) print(p1.__dict__) print(p1.name) del p1.name print(p1.__dict__) p2 = People(123,25,25.5) #name为非字符串时,认为传入有误,不向字典中设置值 print(p2.__dict__)
描述符字符检测(三个变量):
class Typed: def __init__(self,key,expected_type): self.key = key self.expected_type = expected_type def __get__(self,instance,owner): print('get方法') # print("instance参数【%s】",instance) # print("owner参数【%s】"%owner) return instance.__dict__[self.key] def __set__(self, instance, value): #描述符 123-->name-->value--底层字典instance.__dict__[self.key] print("set方法") if not isinstance(value,self.expected_type):#比较字符串类型 # print("传入类型有误") # return raise TypeError("%s 传入类型不是 %s" %(self.key,self.expected_type)) instance.__dict__[self.key] = value def __delete__(self,instance): print("delete方法") # print("instance参数【%s】"%instance) instance.__dict__.pop(self.key) class People: name = Typed("name",str) #name被数据描述符(类属性)代理,所以不直接传入instance底层字典,age和salary未被数据描述符占用,age,salary作为实例属性传入底层字典 age = Typed("age",int) salary = Typed("salary",float) def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary p1 = People("yuyukun",233,45.5) print(p1.__dict__)
3.类装饰器引入帖
https://www.zhihu.com/question/26930016
4.类装饰器基本原理
def deco(func): print("+++++++++++++++++") return func @deco #test=deco(test) def test(): print("test函数运行") test()
def deco(obj): print("+++++++++++++++++++++++++++++++++++++++",obj) obj.x = 1 obj.y = 2 obj.z = 3 return obj @deco #Foo=deco(Foo) class Foo: print("test") f1 = Foo() print(f1) print(Foo.__dict__)
类装饰器增强功能,向底层字典中固定设置值
def Typed(**kwargs): def deco(obj): for key,val in kwargs.items(): setattr(obj,key,val) return obj return deco @Typed(x=1,y=2,z=3) #1.Typed(x=1,y=2,z=3)--->deco 2.@deco--->Foo=deco(Foo) class Foo: pass print(Foo.__dict__) #向底层Foo字典传入x:1,y:2,z:3 @Typed(name="alex") #1.Typed(name="alex")--->deco 2.@deco--->Bar=deco(Bar) class Bar: print(12345) Bar() print(Bar.__dict__)
类装饰器的应用
类装饰器在限定变量类型中的应用:代码分单个部分 1定义描述符类,设置描述符 2添加装饰器和设置两层函数向字典中加入key,value值 3添加装饰器和定义数据属性类
class Typed: def __init__(self,key,expected_type): self.key = key self.expected_type = expected_type def __get__(self,instance,owner): print('get方法') return instance.__dict__[self.key] def __set__(self, instance, value): #描述符 123-->name-->value--底层字典instance.__dict__[self.key] print("set方法") if not isinstance(value,self.expected_type):#比较字符串类型 raise TypeError("%s 传入类型不是 %s" %(self.key,self.expected_type)) instance.__dict__[self.key] = value def __delete__(self,instance): print("delete方法") instance.__dict__.pop(self.key) def deco(**kwargs): #kwargs={"name":str,"age":int} def wrapper(obj): #obj=People for key,val in kwargs.items(): #(("name",str),("age",int)) # print("==============>",key,val) setattr(obj,key,Typed(key,val)) # 上面行代码等同于setattr(People,"name",Typed("name",str)) #装饰器作用,替换这条代码 People.name=Typed("name",str) return obj return wrapper @deco(name=str,age=int) #@wrapper ===> People=wrapper(People) class People: # name = "alex" # name = Typed("name",str) #name被数据描述符(类属性)代理,所以不直接传入instance底层字典,age和salary未被数据描述符占用,age,salary作为实例属性传入底层字典 def __init__(self,name,age,salary,gender,height): self.name = name self.age = age self.salary = salary p1 = People("alex",233,45.5,"x","y") print(People.__dict__)
描述符总结:可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethod,@property,甚至是__slots__属性
描述符是众多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架的一个组件
5.利用描述符自定制property
class Lazyproperty: def __init__(self,func): #数据描述符 print("=======>",func) self.func = func def __get__(self, instance, owner): print("get") print(instance) #instance = <__main__.Room object at 0x000000000290C2E8> print(owner) #Room类 owner = <class '__main__.Room'> res = self.func(instance) return res class Room: x = Lazyproperty("x") def __init__(self, name, width, length): self.name = name self.width = width self.length = length #@property #area = property(area) @Lazyproperty #执行lazyproperty #area=Lazyproperty(area) @称为语法糖 Lazyproperty(area)返回值是实例化得到一个对象 def area(self): return self.width * self.length # print(Room.__dict__) r1 = Room("厕所",1,1) #实例化的结果是Lazyproperty的返回值 # print(r1.__dict__) print(r1.area) #实例属性没有area这个对象,到类中找,类中area被代理,于是触发__get__()方法
描述符,类函数,实例属性调用逻辑,描述符__get__()方法得两参数,instance,owner
描述符用来代理别人属性,别人指类,代理两个方法,1类自己 2类衍生出来的实例
实例属性一旦调用,触发描述符中__get__()方法
实例调用传入,返回值为属性自己
类调用传入值到实例属性,返回值为None
向实例属性字典中添加值的操作,实现延迟功能
class Lazyproperty: def __init__(self,func): self.func = func def __get__(self, instance, owner):#非数据描述符 if instance is None: return self res = self.func(instance) setattr(instance,self.func.__name__,res)#添加值到实例属性中 return res # def __set__(self, instance, value): #添加__set__函数做成数据描述符,调用实例属性时,会经过此数据描述符,而不走__get__()非数据描述符,同时也不向r1.__dict__中添加area值 # print("set") class Room: def __init__(self,name,width,length): self.name = name self.width = width self.length = length @Lazyproperty def area(self): return self.width * self.length @property def area1(self): return self.width * self.length r1 = Room("厕所",1,1) print(r1.area) print(r1.__dict__) print(r1.area) #代码作用,向实例属性中添加area,再次调用r1.area时,可直接从实例属性中调取,而不是从非数据描述符中调取,实现延迟计算 print(r1.area)
6.ClassMethod用法,在类中添加文件
class ClassMethod: def __init__(self,func): self.func = func def __get__(self, 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,x): print("你好啊,帅哥 %s %s %s" %(cls.name,msg,x)) People.say_hi("你是哪偷心的贼",10)
7.人为制作property,启动get_AAA, set_AAA, del_AAA
class Foo: # @AAA.setter def get_AAA(self): print("get的时候运行我啊") # @AAA.setter def set_AAA(self,val): print("set的那时候运行我啊") def del_AAA(self): #(del的那时候运行我) # @AAA.deleter: print("del的时候运行我啊") AAA=property(get_AAA,set_AAA,del_AAA) f1=Foo() #静态属性只有一个值时,不可以设值 f1.AAA f1.AAA="aaa" del f1.AAA
property用法
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() print(obj.price) obj.price = 200 print(obj.price) del obj.price
元类
class Foo: def __init__(self): pass print(Foo) #类名 print(Foo.__dict__) def __init__(self,name,age): self.name = name self.age = age def test(self): print("========>") FFo = type("FFo",(object,),{"x":1,"__init__":__init__,"test":test}) print(FFo) print(FFo.__dict__) f1 = FFo("alex",18) print(f1.name) f1.test()
自定制元类,向实例属性字典中添加内容
思路:1调用实例属性;2进入Foo类,触发MyType类,并返回Foo对象;3执行Foo(XXX)类对象,触发__call__内置属性函数,返回Foo类名
class MyType(type): def __init__(self,a,b,c): pass def __call__(self, *args, **kwargs): print(self) obj = object.__new__(self) #object.__new__(Foo)-->f1 self.__init__(obj,*args,**kwargs) #Foo.__init__(f1,*arg,**kwargs) return obj class Foo(metaclass=MyType): #Foo=MyType(Foo,'Foo',(),{})---》__init__ def __init__(self,name): self.name = name f1 = Foo("alex") #1执行Foo() 2执行__call__ print(f1) print(f1.__dict__)