Python之描述器

Posted 亚洲哈登

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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练习

#StaticMethod
from functools import partial
class StaticMethod:
    def __init__(self,x):
        self.x = x 
    def __get__(self, instance, owner):
        return self.x

class ClassMethod:
    def __init__(self,x):
        self.x = x

    def __get__(self, instance, owner):
        return partial(self.x,owner)

class A:
    @StaticMethod 
    def a():
        print("a")
    @ClassMethod  #b = classmethod(b);
    def b(cls):
        print("b")

A().b()
A.b()
A.a()
A().a()

 

  

 












以上是关于Python之描述器的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向使用 Python 解析 ELF 文件 ( Capstone 反汇编 ELF 文件中的机器码数据 | 创建反汇编解析器实例对象 | 设置汇编解析器显示细节 )(代码片段

Python面向对象学习之八,装饰器

UnityShader之顶点片段着色器Vertex and Fragment ShaderShader资料

VSCode自定义代码片段——CSS选择器

持久片段和查看器

损坏的顶点和片段着色器