一:描述符
1、描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
__get__():调用一个属性时,触发__set__():为一个属性赋值时,触发__delete__():采用del删除属性时,触发2、 描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
class Foo: def __get__(self, instance, owner): print('get方法') def __set__(self, instance, value): print('set方法') instance.__dict__['x']=value #b1.__dict__ def __delete__(self, instance): print('delete方法')
何时何地用 :
描述符应该在另外一个类的类属性定义,请看例子
class Foo: def __get__(self, instance, owner): print('get方法') def __set__(self, instance, value): print('set方法') # instance.__dict__['x']=value #b1.__dict__ def __delete__(self, instance): print('delete方法')# f1=Foo()# f1.name='egon'#自己不会触发#class Bar: x=Foo() #在何地 这就是描述符,x被Foo描述 def __init__(self,x): self.x=xprint(Bar.__dict__)#在何时b1=Bar(10)b1.xb1.x=1del b1.xprint(b1.__dict__) #所以是空
描述符分类:
- 数据描述符 __get__() __set__()
- 非数据描述符 :没有实现__set__()
描述符优先级高低分别是:
1.类属性
2.数据描述符3.实例属性4.非数据描述符5.找不到的属性触发__getattr__()class Foo: def __get__(self, instance, owner): print('get方法') def __set__(self, instance, value): print('set方法') instance.__dict__['x']=value #b1.__dict__ def __delete__(self, instance): print('delete方法')class Bar: x=Foo()print(Bar.x) #get方法没有返回值Bar.x=1print(Bar.__dict__)print(Bar.x) #不会触发get方法,所以类属性比数据描述符有更高优先级print(Bar.__dict__)b1=Bar() #实例化b1.x #getb1.x=1 #setdel b1.x #delete 说明数据描述符比实例属性有更高的优先级#b1=Bar()Bar.x=11111111111111111b1.x #不会触发
class Foo: def __get__(self, instance, owner): print('get方法') # def __delete__(self, instance): # print('delete方法')class Bar: x=Foo()b1=Bar()b1.x=1print(b1.__dict__) #{'x': 1} 说明实例属性比非数据描述符优先级高
class Foo: def __get__(self, instance, owner): print('get方法') # def __delete__(self, instance): # print('delete方法')class Bar: x=Foo()b1=Bar()b1.x=1print(b1.__dict__) #{'x': 1} 说明实例属性比非数据描述符优先级高class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get')class Room: name=Foo() def __init__(self,name,width,length): self.name=name self.width=width self.length=length#name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级#对实例的属性操作,触发的都是描述符的r1=Room('厕所',1,1)r1.name='厨房'print(r1.name) #Noneclass Foo: def __get__(self, instance, owner): print('get')class Room: name=Foo() def __init__(self,name,width,length): self.name=name self.width=width self.length=length#name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级#对实例的属性操作,触发的都是实例自己的r1=Room('厕所',1,1)r1.name='厨房'print(r1.name) #厨房
二:__enter__ 和__exit__
操作文件对象的时候可以这么写:
with open(a.txt) as f:
'代码块'
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
class Open: def __init__(self,name): self.name=name def __enter__(self): print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量') # return self def __exit__(self, exc_type, exc_val, exc_tb): print('with中代码块执行完毕时执行我啊')with Open('a.txt') as f: print('=====>执行代码块') # print(f,f.name)上下文管理协议:enter会在with执行后触发,而exit会在with里面代码块执行完毕触发。
总结:
优点:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__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') print(exc_type) print(exc_val) #异常的值 print(exc_tb) #追踪异常的类 traceback return True # with语句执行完 因为返回True,结束with后接着往下走with Open('a.txt') as f: #触发enter ,其返回值self给f 结束后会触发exit f=obj.__enter__() print('=====>') print(f) print(asdfghjkl) #没有直接报错,一旦有异常直接跳到exit print('---------------')print('0000000000000000')