• Python之描述器


    1.描述器的表现

    用到三个魔术方法,__get__(), __set__(), __delete__()
    方法签名如下
    object.__get__(self,instance,owner)
    object.__set__(self,instance,value)
    object.__delete(self,instance)
    self指代当前实例,调用者
    instance是owner的实例
    owner是属性的所输的类
    
    #描述器A调用并赋值给了类B的属性,当调用类B或者实例b时,去类A执行__get__()函数,类调用instance返回none,实例调用返回实例
    #执行顺序和类,实例字典无关
    class A:
        def __init__(self):
            print(2,'A init')
        def __set__(self, instance, value):
            print(3,self,instance,value)
        def __get__(self, instance, owner): #return值,将会影响b.x or B.x的调用属性
            print(4,self,instance,owner)
    
    class B:
        x = A()
        def __init__(self):
            print(1,'B init')
    
    b = B()  #output 2->1
    b.x   #output 4 <__main__.A object at 0x047B09D0> <__main__.B object at 0x047BB350> <class '__main__.B'>
    B.x   #output 4 <__main__.A object at 0x047B09D0> None <class '__main__.B'>

    此时访问b.x.a1 B.x.a1都会报错 AttributeError:Nonetype
    问题出在__get__的返回值,修改为 return self 返回A的实例就具备了a1属性 返回正常

    class B:
        x = A()
        def __init__(self):
            self.b1 = A()
            print(1,'B init')
    
    b = B()  #output 2->1
    print(b.b1) #output <__main__.A object at 0x03000990> 没有触发__get__的打印
    从运行结果可以看出,只有类属性是类的实例才行

    2.描述其的定义

    python中,一个类实现了__get__,__set__.__delete__的三个方法中的任意一个就是描述器
    1.如果仅仅实现了__get__.就是非数据描述器 non-data descriptor
    2.同时实现了__get__,__set__,就是数据描述器,data descriptor
    如果一个类的类属性,设置为描述器,那么这个类被称为owner属主,method也是类的属性
    
    class A:
        def __init__(self):
            self.a1 = 'a1'
            print(2,'A init')
    
        def __get__(self, instance, owner):
            print(4,self,instance,owner)
            return self
    
    class B:
        x = A()
        def __init__(self):
            self.x = 'b1'   #如果描述器定义了__set__,此时b1就是value
            print(1,'B init')
    
    b = B()  #output 2->1
    print(B.x) #output 4  <__main__.A object at 0x04EEB350> None <class '__main__.B'> ;;;; return <__main__.A object at 0x02E8B350>
    print(B.x.a1) #output 4  <__main__.A object at 0x02E8B350> None <class '__main__.B'> ;;;;return a1
    print(b.x)  #return b1 访问到了实例的属性,而不是描述器
    print(b.x.a1) #AttributeError 'str object has no Attribute
    在非数据描述器中,owner实例的属性 会被实例调用,而不是访问__get__描述器
    #添加了set方法 对比上个代码,;数据描述器
    class A:
        def __init__(self):
            self.a1 = 'a1'
            print(2,'A init')
        def __set__(self, instance, value):  #当定义了set魔术方法后,B实例定义的实例属性self.x = 'b1 不会在写进实例字典,而是调用set方法
            print(3,self,instance,value)
            # instance.__dict__['x']=value
        def __get__(self, instance, owner): #return值,将会影响b.x or B.x的调用属性
            print(4,self,instance,owner)
            # return instance.__dict__['x']
            return self
    class B:
        x = A()
        def __init__(self):
            print(1,'B init')
            print("+++++++++++++++++++++++")
            self.x = 'b1'
            print("+++++++++++++++++++++++")
    
    b = B()  #output 2->1->+ ->3-> + ;;实例化时候,self.x = 'b1'调用了set方法
    print(b.x.a1)  #return a1 直接调用get
    b.x = 100       #return a1 直接调用set
    print(b.__dict__)  #实例字典为空
    print(B.__dict__)

    总结:实例的__dict__优先于非数据描述器;;;数据描述器优先于实例__dict__

    2.1描述器查找顺序和__dict__的关系

    class A:
        def __init__(self):
            self.a1 = 'a1'
            print(2,'A init')
        def __set__(self, instance, value):
            print(3,self,instance,value)
            self.data = value
            print(self.data)
        def __get__(self, instance, owner):
            print(4,self,instance,owner)
            return self
    class B:
        x = A()
        def __init__(self):
            print(1,'B init')
            self.x = 'b.x'
            self.y = 'b.y'
            self.z = 'b.z'
    
    
    b = B()  #output 2->1->+ ->3-> + ;;实例化时候,self.x = 'b1'调用了set方法
    print(b.y) #return b.y
    print(b.x)
    print(B.__dict__)
    print(b.__dict__)#output {'y': 'b.y', 'z': 'b.z'}  ;;;self.x 这里的x是x = A()所以调用set方法,而self.y self.z追加到字典

    2.3练习

    from functools import  partial
    class StaticMethod:
        def __init__(self,fn):
            self.fn = fn
        def __get__(self, instance, owner):
            return self.fn
    class ClassMethod:
        def __init__(self,fn):
            self.fn = fn
        def __get__(self, instance, owner):
            # return self.fn(instance)
            return partial(self.fn,instance)
    class Test:
        @StaticMethod  #st = staticmethod(st) 去调用__get__方法时,不用在考虑这个Test类了 装饰器到描述器执行,调用时加()就返回值
        def st(): #st = return self.fn   
            print('st')
        @ClassMethod
        def ct(cls):    #ct = return self.fn(instance)
            print('ct')
    t = Test()
    Test.st()
    t.st()
    t.ct()
    Test.ct()
    class Typed:
        def __init__(self,name,type):
            self.name = name
            self.type = type
    
        def __set__(self, instance, value):
            if not  isinstance(value,self.type):
                raise TypeError(value)
            instance.__dict__[self.name]=value
    
        def __get__(self, instance, owner):
            if instance is  not None:
                return instance.__dict__[self.name]
            return self
    class Person:
        name = Typed('name',str)   # 1  演变其他的基础
        age = Typed('age',int)
        def __init__(self,name:str,age:int):
            self.name = name       # 2
            self.age = age
    
    p = Person('tom',21)
    print(p.__dict__)       #output {'name': 'tom', 'age': 21}
    print(p.age)            #output 21
    print(Person.age.name)  #output age
    print(Person.name.name) #output name
    print(Typed.__dict__)
    print(Person.__dict__)
    # 1-->类属性调用了类实例,2-->有set,此处调用set方法,因为数据描述器,所以字典为空,自己追加字典值,
    #get也需要从字典中获取值
    import inspect
    class Typed:
        def __init__(self,name,type):
            self.name = name
            self.type = type
    
        def __set__(self, instance, value):
            if not  isinstance(value,self.type):
                raise TypeError(value)
            instance.__dict__[self.name]=value
    
        def __get__(self, instance, owner):
            if instance is  not None:
                return instance.__dict__[self.name]
            return self
    
    def typeassert(cls):
        parmas = inspect.signature(cls).parameters
        for k,v in parmas.items():
            if v.annotation != v.empty:
                setattr(cls,k,Typed(v,v.annotation))
        return cls
    @typeassert
    class Person:
        def __init__(self,name:str,age:int):
            self.name = name       # 2
            self.age = age
    
    p = Person('tom',21)
    本文为原创文章,转载请标明出处
  • 相关阅读:
    java核心学习(二十七) 多线程---线程相关类
    java核心学习(二十六) 多线程---线程池
    java核心学习(二十五) 多线程---线程组和未处理的异常
    java核心学习(二十四) 多线程---线程通信
    java核心学习(二十三) 多线程---线程同步
    java核心学习(二十二) 多线程---线程控制
    模线性方程 poj2115
    求两个圆的重合面积+二分 hdu3264
    求多边形面积 HDU2036
    判断两直线是否相交 hdu1086
  • 原文地址:https://www.cnblogs.com/harden13/p/9038872.html
Copyright © 2020-2023  润新知