一:描述符
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=x print(Bar.__dict__) #在何时 b1=Bar(10) b1.x b1.x=1 del b1.x print(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=1 print(Bar.__dict__) print(Bar.x) #不会触发get方法,所以类属性比数据描述符有更高优先级 print(Bar.__dict__) b1=Bar() #实例化 b1.x #get b1.x=1 #set del b1.x #delete 说明数据描述符比实例属性有更高的优先级 # b1=Bar() Bar.x=11111111111111111 b1.x #不会触发
class Foo: def __get__(self, instance, owner): print('get方法') # def __delete__(self, instance): # print('delete方法') class Bar: x=Foo() b1=Bar() b1.x=1 print(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=1 print(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) #None class 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')