Python基础22 面向对象(5)进阶 上下文管理协议、描述符、类的装饰器、property补充知识、元类

一、上下文管理协议

1、什么是上下文管理协议

上下文管理协议,即with语句。为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__()和__exit__()方法。

2、代码形式

with obj() as f:
   ‘代码块’

3、工作流程

(1)with obj() as f: f=obj.__enter__()
即:obj()是实例化,执行__init__(),再运行obj.__enter__(),赋值给f
(2)执行代码块
①没有异常的情况下,整个代码块运行完毕后去触发__exit__(),它的三个参数都为None。
②有异常的情况下,从异常出现的位置直接触发__exit__()
  a:如果__exit__()的返回值为True,代表吞掉了异常
  b:如果__exit__()的返回值不为True,代表吐出了异常
  c:exit()的的运行完毕就代表了整个with语句的执行完毕

class Foo:
    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 Foo('a.txt') as f:
    print(f)
    print(asdfsaasdfasdfasdfasdfasfasdfasdfasdfasdfasfdasfd)  #触发__exit__
    print(f.name)
    print('-----------------')

print('000000000000000000000000000000000000000000000')

4、用途

①使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预。
②在需要管理一些资源,比如文件、网络链接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制。

二、描述符

1、什么是描述符

描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()方法中的一个,这也被称为描述符协议。必须把描述符定义成这个类的类属性,不能为定义到构造函数中。

2、三个方法的触发

class Str:
    def __get__(self, instance, owner):
        print('Str调用', instance, owner)

    def __set__(self, instance, value):
        print('Str设置', instance, value)

    def __delete__(self, instance):
        print('Str删除', instance)

class People:
    name = Str()

    def __init__(self, name):  # name被Str类代理
        self.name = name

p1 = People('CRTao')    # Str设置 <__main__.People object at 0x000001DA4153AF40> CRTao 触发__set__()方法
p1.name                 # Str调用 <__main__.People object at 0x000001DA4153AF40> <class '__main__.People'> 触发__get__()方法
p1.name = 'CRT'         # Str设置 <__main__.People object at 0x000001DA4153AF40> CRT 触发__set__()方法
del p1.name             # Str删除 <__main__.People object at 0x000001DA4153AF40> 触发__delete__()方法

print(p1.__dict__)  # {}

3、描述符种类

①数据描述符:有__set__()或__delete__()方法
②非数据描述符:只定义了__get__()方法

4、描述符优先级

类属性>数据描述符

class Foo:
    def __get__(self, instance, owner):
        print('get方法')
    def __set__(self, instance, value):
        print('set方法',instance,value)
    def __delete__(self, instance):
        print('delete方法')


class Bar:
    x=Foo()

print(Bar.x)    # get方法 None

Bar.x=1 # 不触发Foo的__set__()方法
print(Bar.x)    # 1 此时类属性x已经被修改,不是描述符Foo了

数据描述符>实例属性

class Foo:
    def __get__(self, instance, owner):
        print('get方法')
    def __set__(self, instance, value):
        print('set方法',instance,value)
    def __delete__(self, instance):
        print('delete方法')


class Bar:
    x=Foo()

b1=Bar()
b1.x   # get方法
b1.x=1 # set方法 <__main__.Bar object at 0x00000284787BAF10> 1
del b1.x # delete方法

实例属性>非数据描述符
非数据描述符>找不到属性,触发__getattr__()

class Foo:
    def __get__(self, instance, owner):
        print('===>get方法')

class Bar:
    x=Foo()
    def  __getattr__(self, item):
        print('触发getattr')

b1=Bar()
b1.x=1
print(b1.__dict__)  # {'x': 1}
b1.xx   # 触发getattr
print(b1.x) # 1

5、描述符小实验:实例化类型检验

class Typed:
    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 = Typed("name",str)
    age = Typed("age",int)

    def __init__(self,name,age):
        self.name = name
        self.age = age

p1 = People("CRTao",18)
print(p1.__dict__)  # {'name': 'CRTao', 'age': 18}
del p1.name
print(p1.__dict__)  # {'age': 18}

三、类的装饰器

1、什么是类的装饰器

一切皆对象,实际上@deco,在对deco(obj)传实参的时候,只要是对象就行,并不检查是否为类或函数。

def deco(obj):
    obj.x = 1

    return obj
@deco #相当于 Foo=deco(Foo),
class Foo:
    pass

print(Foo.x) # 1

2、类的装饰器小实验1:类型检验

class Typed:
    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)

def deco(**kwargs):
    def wrapper(obj):
        for key,val in kwargs.items():
            setattr(obj,key,Typed(key,val))
        return obj
    return wrapper

@deco(name=str,age=int)
class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age

p1 = People("CRTao",18)
print(p1.__dict__)  # {'name': 'CRTao', 'age': 18}
del p1.name
print(p1.__dict__)  # {'age': 18}

类一旦被定义后,__dict__会被冻结为只读状态,不能直接操作它。如:类.__dict__[key]=value会报错。可以直接访问属性并赋值,或者使用 setattr 函数来实现

3、类的装饰器小实验2:利用描述符自定制property

property效果

class Room:
    def __init__(self, width, length):
        self.width = width
        self.length = length

    @property
    def area1(self):
        return self.length * self.width


r1 = Room(10,5)

print(r1.area1) # 50
print(Room.area1)               # <property object at 0x000001E40054F7C0>
print(Room.__dict__["area1"])   # <property object at 0x00000198EA1EF7C0>

模仿property效果,自定制LazyProperty,实现存储已计算结果功能

class LazyProperty:
    def __init__(self,func):
        self.func = func
    def __get__(self,instance,owner):
        print("get")

        if not instance:    # 类调用area,会返回None
            return self
        else:
            res = self.func(instance)
            setattr(instance,self.func.__name__,res)
            return res

class Room:
    def __init__(self, width, length):
        self.width = width
        self.length = length

    @LazyProperty
    def area(self):
        return self.length * self.width


r1 = Room(10,5)

print(r1.area) #  get 50
print(r1.area) # 50
print(Room.area) # <__main__.LazyProperty object at 0x000001861E70CD30>

4、类的装饰器小实验3:自定制类方法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='CRTao'
    @ClassMethod # say=ClassMethod(say)
    def say(cls,age):
        print('%s is %s' %(cls.name,age))

People.say(18)
'''
在这里可以加功能啊
CRTao is 18
'''

p1=People()
p1.say(180)
'''
在这里可以加功能啊
CRTao is 180
'''

四、property补充知识

1、静态属性的增删改查

一个静态属性property本质就是实现了__get__、__set__、__delete__方法,其为数据描述符。

class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')

    @AAA.setter
    def AAA(self,val):
        print('set的时候运行我啊',val)
    @AAA.deleter
    def AAA(self):
        print('del的时候运行我啊')
#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
#定义AAA.setter,AAA.deleter后,可以执行倒数两行代码,否则报错
f1=Foo()
f1.AAA  # get的时候运行我啊
f1.AAA='aaa'    # set的时候运行我啊 aaa
del f1.AAA  # del的时候运行我啊

下述代码效果同上:

class Foo:

    def get_AAA(self):
        print('get的时候运行我啊')
    def set_AAA(self,val):
        print('set的时候运行我啊',val)
    def del_AAA(self):
        print('del的时候运行我啊')

    AAA=property(get_AAA,set_AAA,del_AAA)

f1=Foo()
f1.AAA  # get的时候运行我啊
f1.AAA='aaa'    # set的时候运行我啊 aaa
del f1.AAA  # del的时候运行我啊

2、小实验:用property实现类型检验

class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        return self.nm

    @name.setter
    def name(self,value):
        if not isinstance(value,str):
            raise TypeError('必须是字符串类型')
        self.nm=value

    @name.deleter
    def name(self):
        del self.nm

p1=People('CRTao') #self.name实际是存放到self.nm里
print(p1.name)  # CRTao
del p1.name
print(p1.__dict__)  # {}
# p1.name=1

五、元类

1、什么是元类

类的类,即类的模版。元类的实例是类。
type是Pthon的一个内建元类。pthon中的任何class定义的类,默认都是type的实例化对象。

2、创建类的两种方法

除了使用默认的元类type,用户也可以通过继承type来自定义元类:
type(类名,父类,类的属性字典)
注:由于父类可能是多个,所以要写成元组形式

#方法一
class Foo1:
    x =1
    def func1(self):
        pass

#方法二
def func2(self):
    pass

Foo2 = type("Foo2",(object,),{"func2":func2,"y":2})

3、自定制元类

class MyType(type):
    def __init__(self,a,b,c):
        print(self) # <class '__main__.Foo'>
        print("a:%s,b:%s,c:%s" %(a,b,c))
        # a:Foo,b:(),c:{'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x000001EA705CE280>}
    def __call__(self, *args, **kwargs):
        print("self:%s,args:%s,kwargs:%s" %(self,args,kwargs))
        obj=object.__new__(self) #object.__new__(Foo)--> 产生f1,赋给obj
        self.__init__(obj,*args,**kwargs)  #Foo.__init__(f1,*arg,**kwargs)
        return obj
class Foo(metaclass=MyType):
    '''
    metaclass=MyType 相当于执行 Foo=MyType(Foo,'Foo',(),{}),
    触发MyType的__init__方法
    '''
    def __init__(self,name):
        self.name=name # f1.name=name

f1=Foo('CRTao') # 触发MyType的__call__方法
print(f1) # <__main__.Foo object at 0x00000201E071BFD0>
print(f1.__dict__) # {'name': 'CRTao'}
  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值