Python 的描述符 descriptor详解
|
Python 在 2.2 版本中引入了descriptor(描述符)功能,也正是基于这个功能实现了新式类(new-styel class)的对象模型,同时解决了之前版本中经典类 (classic class) 系统中出现的多重继承中的 MRO(Method Resolution Order) 问题,另外还引入了一些新的概念,比如 classmethod,staticmethod,super,Property 等。因此理解 descriptor 有助于更好地了解 Python 的运行机制。 那么什么是 descriptor 呢? 简而言之:descriptor 就是一类实现了__get__(),__set__(),__delete__()方法的对象。 Orz...如果你瞬间顿悟了,那么请收下我的膝盖; 引言 对于陌生的事物,一个具体的栗子是最好的学习方式,首先来看这样一个问题:假设我们给一次数学考试创建一个类,用于记录每个学生的学号、数学成绩、以及提供一个用于判断是否通过考试的check 函数:
class MathScore():
def __init__(self,std_id,score):
self.std_id = std_id
self.score = score
def check(self):
if self.score >= 60:
return 'pass'
else:
return 'failed'
很简单一个示例,看起来运行的不错: xiaoming = MathScore(10,90) xiaoming.score Out[3]: 90 xiaoming.std_id Out[4]: 10 xiaoming.check() Out[5]: 'pass' 但是会有一个问题,比如手一抖录入了一个负分数,那么他就得悲剧的挂了: xiaoming = MathScore(10,-90) xiaoming.score Out[8]: -90 xiaoming.check() Out[9]: 'failed' 这显然是一个严重的问题,怎么能让一个数学 90+ 的孩子挂科呢,于是乎一个简单粗暴的方法就诞生了:
class MathScore():
def __init__(self,score):
self.std_id = std_id
if score < 0:
raise ValueError("Score can't be negative number!")
self.score = score
def check(self):
if self.score >= 60:
return 'pass'
else:
return 'failed'
xiaoming = MathScore(10,-90)
Traceback (most recent call last):
File "<ipython-input-12-6faad631790d>",line 1,in <module>
xiaoming = MathScore(10,-90)
File "C:/Users/xu_zh/.spyder2-py3/temp.py",line 14,in __init__
raise ValueError("Score can't be negative number!")
ValueError: Score can't be negative number!
OK,但我们还无法阻止实例对 score 的赋值操作,毕竟修改成绩也是常有的事: xiaoming = MathScore(10,90) xiaoming = -10 # 无法判断出错误 对于大多数童鞋,这个问题 so easy 的啦:将 score 变为私有,从而禁止 xiaoming.score 这样的直接调用,增加一个 get_score 和 set_score 用于读写:
class MathScore():
def __init__(self,score):
self.std_id = std_id
if score < 0:
raise ValueError("Score can't be negative number!")
self.__score = score
def check(self):
if self.__score >= 60:
return 'pass'
else:
return 'failed'
def get_score(self):
return self.__score
def set_score(self,value):
if value < 0:
raise ValueError("Score can't be negative number!")
self.__score = value
这确实是种常见的解决方法,但是不得不说这简直丑爆了: 调用成绩再也不能使用 xiaoming.score 这样自然的方式,需要使用 xiaoming.get_score() ,这看起来像口吃在说话! 作为一门简洁优雅的编程语言,Python 是不会坐视不管的,于是其给出了 Property 类: Property 类 先不管 Property 是啥,咱先看看它是如何简洁优雅的解决上面这个问题的:
class MathScore():
def __init__(self,score):
self.std_id = std_id
if score < 0:
raise ValueError("Score can't be negative number!")
self.__score = score
def check(self):
if self.__score >= 60:
return 'pass'
else:
return 'failed'
def __get_score__(self):
return self.__score
def __set_score__(self,value):
if value < 0:
raise ValueError("Score can't be negative number!")
self.__score = value
score = property(__get_score__,__set_score__)
与上段代码相比,主要是在最后一句实例化了一个 property 实例,并取名为 score, 这个时候,我们就能如此自然的对 instance.__score 进行读写了:
xiaoming = MathScore(10,90)
xiaoming.score
Out[30]: 90
xiaoming.score = 80
xiaoming.score
Out[32]: 80
xiaoming.score = -90
Traceback (most recent call last):
File "<ipython-input-33-aed7397ed552>",in <module>
xiaoming.score = -90
File "C:/Users/xu_zh/.spyder2-py3/temp.py",line 28,in __set_score__
raise ValueError("Score can't be negative number!")
ValueError: Score can't be negative number!
WOW~~一切工作正常! class property(fget=None,fset=None,fdel=None,doc=None) #拷贝自 Python 官方文档 实例化 property 实例(我知道这是句废话); “descriptor 就是一类实现了__get__(),__delete__()方法的对象。” @~@ 如果你这次又秒懂了,那么请再次收下我的膝盖 Orz... 另外,Property 还有个装饰器语法糖 @property,其所实现的功能与 property() 完全一样:
class MathScore():
def __init__(self,score):
self.std_id = std_id
if score < 0:
raise ValueError("Score can't be negative number!")
self.__score = score
def check(self):
if self.__score >= 60:
return 'pass'
else:
return 'failed'
@property
def score(self):
return self.__score
@score.setter
def score(self,value): #注意方法名称要与上面一致,否则会失效
if value < 0:
raise ValueError("Score can't be negative number!")
self.__score = value
我们知道了 property 实例的工作方式了,那么问题又来了:它是怎么实现的? descriptor 描述符 照样先不管 descriptor 是啥,咱们还是先看栗子,对于上面 Property 实现的功能,我们可以通过自定义的 descriptor 来实现:
class NonNegative():
def __init__(self):
pass
def __get__(self,ist,cls):
return 'descriptor get: ' + str(ist.__score ) #这里加上字符描述便于看清调用
def __set__(self,value):
if value < 0:
raise ValueError("Score can't be negative number!")
print('descriptor set:',value)
ist.__score = value
class MathScore():
score = NonNegative()
def __init__(self,score):
self.std_id = std_id
if score < 0:
raise ValueError("Score can't be negative number!")
self.__score = score
def check(self):
if self.__score >= 60:
return 'pass'
else:
return 'failed'
我们新定义了一个 NonNegative 类,并在其内实现了__get__、__set__方法,然后在 MathScore 类中实例化了一个 NonNegative 的实例 score,注意!!!重要的事情说三遍:score 实例是 MathScore 的类属性!!!类属性!!!类属性!!!这个 Mathscore.score 属性同上面 Property 的 score 实例的功能是一样的,只不过 Mathscore.score 调用的 get、set 并不定义在 Mathscore 内,而是定义在 NonNegative 类中,而 NonNegative 类就是一个 descriptor 对象! 纳尼? NonNegative 类的定义中可没见到半个 “descriptor” 的字样,怎么就成了 descriptor 对象??? 淡定! 重要的事情这里只说一遍:任何实现 __get__,__set__ 或 __delete__ 方法中一至多个的类,就是 descriptor 对象。所以 NonNegative 自然是一个 descriptor 对象。 (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
