ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python描述符的一些补充

2022-06-23 01:01:35  阅读:161  来源: 互联网

标签:__ .__ Python self 补充 描述符 实例 属性


最近有空在看新版的《Effective Python》一书,看到了一些描述符的使用。

补充一些我的一些自己看法

 

当一个实例成为一个类的属性时,当这个类有__get__属性,__set__属性,这个类的实例成为类的属性就称为描述符

我们的日常使用中,函数就属于描述符。因为创建函数的类有__get__属性

首先,我来解释一下,覆盖性与非覆盖型的描述符区别

覆盖与非覆盖,是相对实例属性来说的

# 创建描述符
class Quantiy:
    # 外部定一个描述符类属性
    __count = 0

    def __init__(self):
        # 描述符初始化赋值
        cls = self.__class__
        prefil = cls.__name__
        index = cls.__count
        # 设立一个独一无二的名称
        self.storage = '_{}#{}'.format(prefil, index)
        cls.__count += 1

    def __get__(self, instance, owner):
        return 'This is quantiy'


class T1:
    q = Quantiy()

    def __init__(self):
        self.q = 1
        self.hello = 666

    def hello(self):
        return 'h_func'

if __name__ == '__main__':
    t = T1()
    print(t.q)
    print(t.hello)

  

上面的代码运行,输出还是self的实例属性,实例通过点的方式取属性,还是获取实例自身__dict__中的属性

# 创建描述符
class Quantiy:
    # 外部定一个描述符类属性
    __count = 0

    def __init__(self):
        # 描述符初始化赋值
        cls = self.__class__
        prefil = cls.__name__
        index = cls.__count
        # 设立一个独一无二的名称
        self.storage = '_{}#{}'.format(prefil, index)
        cls.__count += 1

    def __get__(self, instance, owner):
        return 'This is quantiy'

    def __set__(self, instance, value):
        ...

class T1:
    q = Quantiy()

    def __init__(self):
        self.q = 1
        self.hello = 666

    def hello(self):
        return 'h_func'

if __name__ == '__main__':
    t = T1()
    print(t.q)
    print(t.hello)

  

当在描述符中加入了__set__之后,对于实例通过点的方式取值或者设置同名的实例属性时,会设置该类的同名属性优先,也就是对描述符[实例]进行操作

后面经过研究,其实还挺复杂的逻辑,网上找到一个坐着写的挺好的。

参考链接:https://halfclock.github.io/2019/06/04/python-descriptor_02/

他对覆盖型与非覆盖型的描述符有着明确的说明

各类描述符的使用场景

全覆盖型描述符

这里指实现了 __set__ 和 __get__ 协议的描述符。

能够使用全覆盖型描述符的场景,通常还需要考虑是否使用特性。这部分可以参照上一篇博文最后的总结。

此类描述符还有另一个使用的场景,即只读属性,只读属性的 __set__ 只需要抛出指定的异常即可。

必须设置 __get__ 的原因是,防止用户使用 __dict__ 直接对实例属性进行修改,因为覆盖型描述符不管是否有实例属性,在读值时都会访问 __get__ 方法。

半覆盖型描述符

这里指没有 __get__ 方法的覆盖型描述符。

此类描述符通常用于验证属性。

即检查用户给的 value 是否符合系统定义的规则,如果符合规则,才将之存储至实例属性中,当需要拿到实例属性时,不用通过 __get__ ,直接访问实例属性即可能快速的拿到需要的值。

非覆盖型描述符

这里指没有 __set__ 方法的覆盖型描述符。

此类描述符适合使用在第一次访问需要加载数据(花费时间长)的场景。———— 高效缓存

因为第一次访问实例属性时,调用描述符实例的 __get__ 方法,在该方法中加载数据,然后将加载完成的数据(value),使用 obj. attr = value 赋给实例属性。

之后再访问实例属性就无需加载数据,不再访问描述符实例的 __get__ 方法了,直接访问实例属性即可。

总结

本篇博文与上一篇博文总结了属性描述符是什么、怎么使用、以及何时使用的问题。

指出了属性描述符是实现了描述符协议的类、其实例通常被托管类类属性所承载、并且根据是否实现 __set__ 方法,分为覆盖型描述符和非覆盖型描述符,他们分别应用于只读属性、属性验证和高效缓存中。

 以上的内容,我是复制了它的博客内容,确实让我对Python的描述符有了更加深入的认识。     下面是我对描述符使用的增强案例
# 创建描述符
class Quantiy:
    # 外部定一个描述符类属性
    __count = 0

    def __init__(self):
        # 描述符初始化赋值
        cls = self.__class__
        prefil = cls.__name__
        index = cls.__count
        # 设立一个独一无二的名称
        self.storage = '_{}#{}'.format(prefil, index)
        cls.__count += 1

    def __get__(self, instance, owner):
        return instance.__dict__[self.storage + str(id(instance))]

    def __set__(self, instance, value):
        # 托管属性 描述符示例操作托管示例对属性进行赋值
        if value > 0:
            # 增强属性赋值,通过计算器与实例的id作为每个实例的属性唯一码
            instance.__dict__[self.storage + str(id(instance))] = value
        else:
            raise ValueError('value must be > 0')


class LineItem:
    # 描述符实例赋值给托管类属性
    weight = Quantiy()
    price = Quantiy()

    def __init__(self, description, weight, price):
        # 储存实例,托管实例中存储自身托管属性的属性 self.weight与self.price
        self.description = description
        # 这个按照书中的说法叫特性的赋值方法了,不是属性赋值了。
        self.weight = weight
        self.price = price

    @property
    def subtoall(self):
        return self.weight * self.price

  

上面的代码是摘抄至流畅的Python一书,由于Python模块在导入执行后,类的描述符只会初始化一次,通过描述符的加工器给实例属性的__dict__中赋值属性我给加上了实例的id码,做唯一。

这样,基本确保了每次实例出来的对象通过获取的属性不会出问题

前面我的理解错了,其实对于不同的实例,通过描述符赋值相同的属性名是正确的,这并不会影响各个实例的属性之间干扰

# 创建描述符
class Quantiy:
    # 外部定一个描述符类属性
    __count = 0

    def __init__(self):
        # 描述符初始化赋值
        cls = self.__class__
        prefil = cls.__name__
        index = cls.__count
        # 设立一个独一无二的名称
        self.storage = '_{}#{}'.format(prefil, index)
        cls.__count += 1

    def __get__(self, instance, owner):
        # return instance.__dict__[self.storage + str(id(instance))]
        return instance.__dict__[self.storage]

    def __set__(self, instance, value):
        # 托管属性 描述符示例操作托管示例对属性进行赋值
        if value > 0:
            # 增强属性赋值,通过计算器与实例的id作为每个实例的属性唯一码
            # instance.__dict__[self.storage + str(id(instance))] = value
            instance.__dict__[self.storage] = value
        else:
            raise ValueError('value must be > 0')


class LineItem:
    # 描述符实例赋值给托管类属性
    weight = Quantiy()
    price = Quantiy()

    def __init__(self, description, weight, price):
        # 储存实例,托管实例中存储自身托管属性的属性 self.weight与self.price
        self.description = description
        # 这个按照书中的说法叫特性的赋值方法了,不是属性赋值了。
        self.weight = weight
        self.price = price

    @property
    def subtoall(self):
        return self.weight * self.price

  这样是对的

如果通过秒速符对实例的__dict__属性进行操作,可以理解为描述符就是一个工具而已,上面的方式,没有用到描述符实例内部的属性来保存相对托管类的实例的属性。

确实非常不错

 

下面是我摘抄至《Effective Python》书中使用描述的方式,他直接将描述符实例放入托管类,并且没有进行__init__的托管类实例化函数的操作

所以他相关的实例属性[伪],其实托管类的实例__dict__中还是空的

# Example 14
from weakref import WeakKeyDictionary

class Grade:
    def __init__(self):
        self._values = WeakKeyDictionary()

    def __get__(self, instance, instance_type):
        if instance is None:
            return self
        return self._values.get(instance, 0)

    def __set__(self, instance, value):
        if not (0 <= value <= 100):
            raise ValueError(
                'Grade must be between 0 and 100')
        self._values[instance] = value


# Example 15
class Exam:
    math_grade = Grade()
    writing_grade = Grade()
    science_grade = Grade()

first_exam = Exam()
first_exam.writing_grade = 82
second_exam = Exam()
second_exam.writing_grade = 75
print(f'First  {first_exam.writing_grade} is right')
print(f'Second {second_exam.writing_grade} is right')
print(first_exam.__dict__)
print(second_exam.__dict__)

  

两种方式的操作,我还是更加喜欢第一种,话说流畅的Python一书确实很多操作与解释很棒,最近太懒了。哈哈,要么等新版的流畅的Python一书出来,再看一本

希望我英语能够学好,下次直接看新版的英文原书

 

 

   

标签:__,.__,Python,self,补充,描述符,实例,属性
来源: https://www.cnblogs.com/sidianok/p/16403784.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有