首页 > 编程语言 > 详细

Python描述符

时间:2019-11-22 20:54:18      阅读:84      评论:0      收藏:0      [点我收藏+]

描述符的定义

描述符是Python中一种特殊的语法,它是对多个属性利用相同存取逻辑的一种方式。描述符是实现了特定协议的类,这个协议包括实现__get__,__set__和__delete__方法,不过也可以只实现一部分协议。

描述符的简单例子

class Quantity(object):
    def __init__(self, name):
        self.name = name
    
    def __get__(self, instance, owner):
        return instance.__dict__[self.name]
    
    def __set__(self, instance, val):
        instance.__dict__[self.name] = val 
    

class Obj(object):
    weight = Quantity("weight")
    price = Quantity("price")
    
    def __init__(self, weight, price):
        self.weight = weight
        self.price = price
        
obj = Obj(1,2)
print obj.weight, obj.__dict__

输出为:
1 {‘price‘: 2, ‘weight‘: 1}
在以上的代码中,Quantity就是一个描述符,它实现了__get__、__set__方法,它其实是一个类属性。在访问类的对象时,会优先访问同名描述符的对象,并调用相应的__get__和__set__方法。
在__get__函数中,参数instance是类的对象,owner是类本身;__set__函数中,instance是类对象,val则是传入的值。描述符中存储了对象成员的名称,通过instance的__dict__来访问和存取相应的对象成员。

有人会觉得上图中给描述符命名的方法略显繁琐,毕竟名称可能会输入错误,那么有没有不用手动给描述符传入名称的初始化方法呢?当然有咯!直接上代码:

class Quantity(object):
    count=0
    def __init__(self):
        self.name = '{}'.format(Quantity.count)
        Quantity.count += 1
    
    def __get__(self, instance, owner):
        return getattr(instance, self.name)
    
    def __set__(self, instance, val):
        setattr(instance, self.name, val) 
    

class Obj(object):
    weight = Quantity()
    price = Quantity()
    
    def __init__(self, weight, price):
        self.weight = weight
        self.price = price
        
obj = Obj(1,2)
print obj.weight, obj.__dict__, Obj.__dict__

输出为:

1 {'1': 2, '0': 1} {'__module__': '__main__', 'weight': <__main__.Quantity object at 0x03C79450>, 'price': <__main__.Quantity object at 0x03C79470>, '__dict__': <attribute '__dict__' of 'Obj' objects>, '__weakref__': <attribute '__weakref__' of 'Obj' objects>, '__doc__': None, '__init__': <function __init__ at 0x03C7DD30>}

从中可以看到,我们能给描述符传入任意的名称,当我们访问obj的weight时,会触发Obj的weight描述符的__get__和__set__函数,实际操作的则是obj __dict__中的‘0’号对象。
另外,通过查看Obj的__dict__对象,我们也能了解其实描述符就是一种类属性。只不过它的函数能传入类的instance作为参数,以此来访问类对象的属性而已。

覆盖型描述符与非覆盖型描述符

描述符分为覆盖型和非覆盖型的,覆盖型与非覆盖型的区别在于是否实现了__set__函数。
实现了__set__函数的话,我们对类对象的属性赋值时,就会优先访问它相应描述符的__set__函数;没有实现__set__函数的话,对类对象成员进行赋值则会将描述符遮盖:

class Quantity(object):
    count=0
    def __init__(self):
        self.name = '{}'.format(Quantity.count)
        Quantity.count += 1
    
    def __get__(self, instance, owner):
        print "666"
        return 2
    
    def __set__(self, instance, val):
          setattr(instance, self.name, val) 
    

class Obj(object):
    weight = Quantity()
    price = Quantity()
    
    def __init__(self, weight, price):
        pass
        
obj = Obj(1,2)
obj.weight = 3
print obj.weight, obj.__dict__

输出为:

666
2 {'0': 3}

可以看到,当存在覆盖型描述符时,对weight的赋值会直接调用weight描述符的__set__方法,实际更改的是__dict__中的"0"号对象。
让我们再看看非覆盖型的:

class Quantity(object):
    count=0
    def __init__(self):
        self.name = '{}'.format(Quantity.count)
        Quantity.count += 1
    
    def __get__(self, instance, owner):
        print "666"
        return 2
    

class Obj(object):
    weight = Quantity()
    price = Quantity()
    
    def __init__(self, weight, price):
        pass
        
obj = Obj(1,2)
print obj.weight
obj.weight = 3
print obj.weight, obj.__dict__

输出为:

666
2
3 {'weight': 3}

可以看到,当我们只是访问weight时,会调用描述符的__get__方法;而当我们为weight赋值时,程序会在对象的__dict__里创建一个属性,并覆盖描述符。

类的方法也是一种描述符

值得一提的是,类中定义的方法其实也是一种描述符。方法自己是function对象,定义了__get__函数(没有定义__set__,因此它们是非覆盖型描述符),当我们调用类对象的方法成员时,__get__函数调用,并返回一个绑定的方法对象(bound method),该对象的__func__成员即是真正引用的原始函数。通过调用绑定方法对象的__call__方法,来调用__func__,最终将函数执行起来。

最后感谢刘佳卉大佬建议我用Markdown来写博客。

Python描述符

原文:https://www.cnblogs.com/wickedpriest/p/11913593.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!