• 描述符应用与类的装饰器


    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__)
    
    
     
  • 相关阅读:
    LeetCode 93. Restore IP Addresses
    LeetCode 92. Reverse Linked List II
    LeetCode 94. Binary Tree Inorder Traversal
    javaweb中重定向和请求转发(response.sendRedirect()和request.getRequestDispatcher(rul).forward(request,response)))的区别
    java关于jdbc的配置与使用步骤
    关于php中的include html文件的问题,为什么html可以在php中执行
    yii2 无法显示debug条的问题解决方法
    elasticsearch报错expected <block end>, but found BlockMappingStart解决方法
    sysctl -p 报错问题的解决方法
    yii2 Rbac使用yii命令一键建表
  • 原文地址:https://www.cnblogs.com/yuyukun/p/10684609.html
Copyright © 2020-2023  润新知