Effective Python Ver2.0_StudyNotes_用描述符来改写需要复用的@property方法
用描述符来改写需要复用的@property方法
@property机制的最大缺点是不方便复用,其不能把他修饰的方法所使用的逻辑,套用在同一个类的其他属性上面,也不能在无关的类里面复用。
要实现复用,可以通过描述符实现:
描述符协议规定了程序应该如何处理属性访问操作,充当描述符的那个类能够实现__get__与__set__方法,这样其他类就可以共用这个描述符而实现已有逻辑的复用
先简单说说描述符的内部运行机制:
示例代码:
class Grade:
def __init__(self):
self._values = 0
def __get__(self, instance, instance_type):
return self._values
def __set__(self, instance, value):
if not (0 <= value <= 100):
raise ValueError("Value must between 0 and 100")
self._values = value
class Exam:
math_grade = Grade()
math_science = Grade()
if __name__ == '__main__':
exam = Exam()
exam.math_grade = 40
exam.math_grade
Exam实例进行赋值操作时候,Python会把这次赋值操作转译成:Exam.__dict__['math_grade'].__set__(exam, 40)
同样的,进行获取属性操作,Python会将此次获取操作转译成:Exam.__dict__['math_grade'].__get__(exam)
其实,这样的转译效果是由object的__getattribute__方法促成的,通俗的说就是当Exam实例里面没有math_grade的属性时候,Python会转而在类的层面查找,查询Exam类里面有没有这样一个属性,如果有,而且还是实现了__get__与__set__方法的对象,那么系统就认定你通过描述符协议定义了这个属性的访问行为。
进一步应用
以上简单的示例还不完善,稍加完善的代码可以解决一些常见的问题,比如:Exam每次实例化的对象访问的math_grade其实是同一个Grade实例,这里需要做区分操作,最直接的方法时self._values={},使用不同的Exam实例去获取对应的values值,当然随之出现的另一个问题就是--内存泄漏。
大致说下内存泄漏的原因:在程序运行时,传给__set__方法的那些Exam实例全部会被Grade之中的_values字典所引用(因为_values字典里面存储的就是以实例为key的键值对),于是,指向那些实例的引用数量永远都不会降为0,这就导致垃圾回收机制没办法将Exam的实例清除掉。
解决这个问题的一个方式是:可以使用Python内置weakref模块,该模块中有一种特殊的字典,名为WeakKeyDictionary,他可以取代_values的普通字典。这个字典的特殊之处在于:如果运行时系统发现,指向Exam实例的引用只剩下一个(也就是只有Grade里面的_values的字典在使用其作为key的引用,而其他地方并没有再使用这个实例了),同时这个引用又是由WeakKeyDictionary的key所发起的,那么系统会将该引用从这个特殊字典里面删除,于是指向那个Exam实例的引用降为0,垃圾回收机制可以被触发,以防止内存的泄漏。
最后的完善代码,以作参考:
from weakref import WeakKeyDictionary
class Grade:
def __init__(self):
self._values = WeakKeyDictionary()
def __get__(self, instance, instance_type):
if instance is None:
return None
return self._values.get(instance, 0)
def __set__(self, instance, value):
if not (0 <= value <= 100):
raise ValueError("Grade must between 0 and 100")
self._values[instance] = value
class Exam:
math_grade = Grade()
science_grade = Grade()
if __name__ == '__main__':
first_exam = Exam()
first_exam.math_grade = 82
first_exam.science_grade = 99
second_exam = Exam()
second_exam.math_grade = 75
second_exam.science_grade = 100
print(f"First math_grade {first_exam.math_grade} is right")
print(f"Second math_grade {second_exam.math_grade} is right")
请相信自己
当我们迷茫,懒惰,退缩的时候 我们会格外的相信命运 相信一切都是命中注定
而当我们努力拼搏,积极向上时 我们会格外的相信自己
所以命运是什么呢? 它是如果你习惯它 那它就会一直左右你
如果你想挣脱它 那它就成为你的阻碍 可如果你打破了它 那它就是你人生的垫脚石!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!