首页 > 编程语言 >[Python] 用描述符实现复用@property方法

[Python] 用描述符实现复用@property方法

时间:2023-01-29 00:33:19浏览次数:45  
标签:__ Python self value 描述符 values ._ property def

1 Python内置的@property机制弊端在于不方便复用

不能把它所修饰方法中的逻辑,套用在同一个类中的其他属性上。

  • 例如,编写一个类记录分数
class Grade:
    def __init__(self):
        self._values = 0
    
    @property
    def grade(self):
        return self._values

    @grade.setter
    def grade(self, value):
        if isinstance(value, int):
            self._values = value
  • 假设,还有一个类,用于记录各个体育项目的分数
class Exam:
    def __init__(self):
        self._tennis = 0
        self._ping_pong = 0
    
    @property
    def tennis(self):
        return self._values

    @tennis.setter
    def tennis(self, value):
        if isinstance(value, int):
            self._values = value

    @property
    def ping_pong (self):
        return self._values

    @ping_pong .setter
    def ping_pong (self, value):
        if isinstance(value, int):
            self._values = value

显然,在这个类中,针对每一个体育项目,都要一套@property方法,但其实每个该方法的逻辑都是一模一样的。针对这个问题,Python提供了描述符实现。

用描述符方法实现复用

Python中,描述符协议规定了程序如何处理属性访问操作。
充当描述符的类要实现__get____set__方法,他们分别对应获取属性、赋值属性操作。

  • 以描述符的方式,重写Grade
class Grade:
    def __init__(self):
        self._values = 0

    def __get__(self, instance, instance_type):
        return self._values

    def __set__(self, instance, value):
        if isinstance(value, int):
            self._values = value
  • 重新定义Exam类,采用类级别的属性来实现分数的访问
class Exam:
    tennis= Grade()
    ping_pong  = Grade()
  • 使用这两个类完成各个项目的分数记录
exam = Exam()
exam.tennis = 99
exam.ping_pong = 100

实际上,Grade的写法不对,会造成一些奇怪的现象,如下:

first_exam = Exam()
sec_exam = Exam()
first_exam.tennis = 90
sec_exam.tennis = 100
print(first_exam.tennis)
print(sec_exam.tennis)

>>>
100
100

出现这种现象的原因在于:Grade只在定义Exam类时构造一次,而创建的Exam的每个实例中的tennis属性作为指针都指向了那个Grade。这导致改变不同Exam实例中的相同属性,最终都修改了该属性指向的Grade实例

2.1 解决方法

把每个Exam实例在这个属性上的值都记录下来,为防止内存泄漏,可使用weakref模块提供的WeakKeyDictionary来实现每个实例的状态保存。

from weakref import WeakKeyDictionary

class Grade1:
    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

在使用普通字典的情况下,每个Exam实例都会被Grade中的_values字典引用,导致垃圾回收器不会自动释放这些实例,在一直使用该类的情况下,导致内存泄漏,占满整个堆区。
在使用这种特殊字典的情况下,可防止内存泄漏,具体原理咱就不清楚了。

标签:__,Python,self,value,描述符,values,._,property,def
From: https://www.cnblogs.com/hepang/p/17071577.html

相关文章

  • 【Python基础学习】7.文件和数据格式化
    主要参考来源:慕课嵩天老师的“Python语言程序设计”[https://www.icourse163.org/course/BIT-268001?tid=1468130447]格式化包括字符串格式化和数据格式化字符串格式化:......
  • Python批量改文件名
    对以下路径中的文件名批量修改。一、读取指定路径中的文件名#导入标准库importos#读取文件名filesDir="路径……"fileNameList=os.listdir(filesDir)#输......
  • python-Couldn‘t find a tree builder with the features you requested: lxml
    执行BeautifulSoup(content,features='lxml')时报错,按照网上的方法安装lxml、重新安装lxml、安装指定版本lxml,都无效。最后发现只是PyCharm设置中project的pyth......
  • Python语言基础—常用运算符总结
    系列文章目录......
  • 2021年最新Python讲义:面向对象(OOP)基本概念
    面向对象(OOP)基本概念面向对象编程——​​ObjectOrientedProgramming​​​简写​​OOP​​目标了解面向对象基本概念01.面向对象基本概念我们之前学习的编程方......
  • 终于把Python库全部整理出来了,非常全面!
    Python库汇总篇!建议先马后看~文章目录​​前言​​​​学习爬虫需要掌握哪些库呢?​​​​通用​​​​网络爬虫框架​​​​HTML/XML解析器​​​​浏览器自动化与仿真​​......
  • Python 中的模块
    Python模块是一个Python文件,定义了各种功能接口。把复杂的功能封装为模块(又称为库),将功能实现的细节隐藏起来,使用该模块(库)的程序员不需要了解实现的细节。通过调用模块封......
  • Python基础——第一部分
    x进制类型定义\(0b****\):用二进制定义整型\(0o****\):用八进制定义整型\(0x****\):用十六进制定义整型a=0b1111b=15c=0o17d=0x0F输出:转换\(bin()\):......
  • 在 Python 中将一个 Legendre 系列添加到另一个 Legendre 系列
    要将一个Legendre系列添加到另一个系列,请使用Python中的polynomial.legendre.legadd()方法嘟嘟。该方法返回一个数组,表示其总和的勒让德系列。返回两个勒让德系列c1+......
  • 为什么你应该使用NumPy数组而不是嵌套的Python列表?
    在本文中,我们将向您展示为什么使用NumPy数组而不是嵌套的Python列表,以及它们之间的异同。PythonNumPyLibraryNumPy是一个Python库,旨在有效地处理Python中的数组。它快......