资讯专栏INFORMATION COLUMN

Python中的属性描述符

geekzhou / 1094人阅读

摘要:下面我们用描述符来实现中的动态属性和特性中提及的订单结算代码第四版使用描述符实现订单结算功能描述符基于协议实现,无需创建子类。特性是覆盖型描述符。非覆盖型描述符没有实现方法的描述符属于非覆盖型描述符。类中定义的方法是非覆盖型描述符。

导语:本文章记录了本人在学习Python基础之元编程篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。

本文重点:

1、了解描述符的定义,功能,协议和用法;
2、了解覆盖型描述符和非覆盖型描述符的概念和区别;
3、了解描述符的用法建议。
一、描述符

描述符:是实现了特定协议的类。
描述符功能:是对多个属性运用相同存取逻辑的一种方式。
描述符协议:包括__get__、__set__、和__delete__方法。通常只实现部分协议。大多数描述符只实现了__get__和__set__方法,但是property类实现了完整的描述符协议。

下面我们用描述符来实现Python中的动态属性和特性中提及的订单结算代码:

第四版:使用描述符实现订单结算功能

class Quantity:#描述符基于协议实现,无需创建子类。

    def __init__(self,storage_name):
        self.storage_name=storage_name#storage_name是托管实例中存储值的属性的名称。

    def __set__(self, instance, value):#重要!instance是LineItem实例,self是描述符实例。
        if value > 0:
            instance.__dict__[self.storage_name]=value#此处必须直接存入__dict__,否则使用setattr函数会导致无限递归。
        else:
            raise ValueError("Value must be > 0")
class LineItem:
    weight = Quantity("weight")#将描述符实例绑定到weight属性。
    price = Quantity("price")#同上。

    def __init__(self,description,weight,price):
        self.description=description
        self.weight=weight
        self.price=price

    def subtotal(self):
        return self.weight*self.price

小结:描述符类的实例能用作托管类的属性,这一点很重要!
不过在上文中的托管类定义体中,实例化描述符如果能按照weight = Quantity()这种格式声明就更好了。为此我们需要写一版自动获取存取属性名称的代码。

第五版:改进描述符类——自动获取存取属性名称

class Quantity:#改进版描述符类
    __counter = 0 

    def __init__(self):
        cls = self.__class__ 
        prefix = cls.__name__
        index = cls.__counter
        self.storage_name = "_{}#{}".format(prefix, index) #每个描述符实例的属性名称都是独一无二的。
        cls.__counter += 1 

    def __get__(self, instance, owner): #此处owner参数是托管类LineItem。
        return getattr(instance, self.storage_name) #从instance中获取储存属性的值。

    def __set__(self, instance, value):
        if value > 0:
            setattr(instance, self.storage_name, value) #使用setattr把值储存在instance中。
        else:
            raise ValueError("value must be > 0")

class LineItem:#托管类
    weight = Quantity()
    price = Quantity()

    def __init__(self,description,weight,price):
        self.description=description
        self.weight=weight
        self.price=price

    def subtotal(self):
        return self.weight*self.price

Tips:通常,我们不会在使用描述符的模块中定义描述符,而是在一个多带带的实用工具模块中定义,以便在整个应用中使用。

二、特性工厂函数与描述符类比较

我们在Python中的动态属性和特性中提到过,抽象定义特性的方式有两种,一是使用特性工厂函数,二是使用描述符类。
现在来对两种方式的优点进行对比辨析:

特性工厂函数:模式简单。

描述符类:模式可拓展。可通过子类共享代码,构建具有部分相同功能的专用描述符,应用更广泛。

个人建议当两种模式均能实现目标时,推荐使用描述类。

三、覆盖型描述符与非覆盖型描述符对比

覆盖型描述符:实现__set__方法的描述符属于覆盖型描述符。
特性是覆盖型描述符。
非覆盖型描述符:没有实现__set__方法的描述符属于非覆盖型描述符。
类中定义的方法是非覆盖型描述符。
小结:如果设置了同名实例属性,对于非覆盖型描述符而言会被覆盖;对于没有实现__get__方法的覆盖型描述符而言,在读操作时描述符对象也会被覆盖。

四、描述符用法建议

使用特性以保持简单:创建只读属性最简单的方式是使用特性。

只读描述符必须有__set__方法:只读描述符必须定义__get__和__set__两个方法,只读属性的__set__方法只需抛出AttributeError异常,并提供合适的错误信息。

用于验证的描述符可以只有__set__方法:描述符用于验证属性时可以不实现__get__,这样从实例中读取同名属性的速度很快。

仅有__get__方法的描述符可以实现高效缓存。

非特殊的方法可以被实例属性覆盖。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41477.html

相关文章

  • python 描述解析

    摘要:之所以是这样是因为当访问一个实例描述符对象时,会将转换为。而类的字典中则有描述符对象。这主要就是因为描述符优先。此外,非数据描述符的优先级低于实例属性。参考以上就是本人对描述符的一些理解,有什么不正确的地方还请不吝指出,谢谢 什么是描述符 python描述符是一个绑定行为的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有 __get__(), __set__(), 和...

    rozbo 评论0 收藏0
  • [译] 属性访问、特性和描述 2

    摘要:不像其他属性,描述符在类级别上创建。当所有者类被定义时,每个描述符对象都是被绑定到一个不同的类级别属性的描述符类实例。这必须返回描述符的值。此外,描述符对有一个方便的响应和请求格式。 注:原书作者 Steven F. Lott,原书名为 Mastering Object-oriented Python __getattribute__()方法 __getattribute__()方法是...

    CloudwiseAPM 评论0 收藏0
  • Python中的描述

    摘要:解答三个问题,描述是什么如何实现使用场景一什么是描述符描述符就是一个具有绑定行为的对象属性,其属性访问将由描述符协议中的方法覆盖。如果这些方法中的任何一个针对某个对象定义,那么它就被认为是一个描述符。 解答三个问题,描述是什么?如何实现?使用场景? 一、什么是描述符 描述符就是一个具有绑定行为的对象属性,其属性访问将由描述符协议中的方法覆盖。这些方法为 __get__、__set__ ...

    Aomine 评论0 收藏0

发表评论

0条评论

geekzhou

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<