Python基础第21天

一:描述符

      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  #不会触发
1/2/3之间的优先级级比较
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')
原文地址:https://www.cnblogs.com/xyd134/p/6561978.html