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
字典引用,导致垃圾回收器不会自动释放这些实例,在一直使用该类的情况下,导致内存泄漏,占满整个堆区。
在使用这种特殊字典的情况下,可防止内存泄漏,具体原理咱就不清楚了。