Python描述符

来源:互联网 发布:淘宝店主图怎么做 编辑:程序博客网 时间:2024/05/17 02:38

看书看到了描述符,没看懂,网上找了几篇文章总结了下。

描述符是一个“绑定行为”的对象属性,在描述符协议中,可以通过方法重写属性的访问。这些方法有get(), set(), delete(),如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符。

Python中没有私有变量,而描述符和属性这些用来是写类似的功能。
描述符被分配给一个类,而不是实例,修改此类,会覆盖或者删除描述符本身,而不是触发它的代码。


首先看一下property类

class MathScore():  def __init__(self, std_id, 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__)

此时可以直接调用score属性

xiaoming = MathScore(10, 90)xiaoming.scoreOut[30]: 90xiaoming.score = 80xiaoming.scoreOut[32]: 80xiaoming.score = -90Traceback (most recent call last): File "<ipython-input-33-aed7397ed552>", line 1, 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!

class property(fget=None, fset=None, fdel=None, doc=None)

工作流程:

  • 实例化property实例 调用property实例会直接调用fget
  • 对property实例进行赋值操作会直接调用fset,并由fset定义完成相应操作
  • 删除property实例,会调用fdel删除实例
  • doc是说明文档
  • 如果只设置了fget,则该实例为只读对象

property还有个装饰器语法糖@property,所实现的功能与property()完全一样:

class MathScore():  def __init__(self, std_id, 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实现的。
但是property有一个问题在于,不能复用,如果我们需要对std_id也进行检查,保证它大于0,那么还需要写一个std_id的property,这样代码就不简洁了。


这时候就需要描述符了。

The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, a.x has a lookup chain starting with a.dict[‘x’], then type(a).dict[‘x’], and continuing through the base classes of type(a) excluding metaclasses. If the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead. Where this occurs in the precedence chain depends on which descriptor methods were defined

用装饰符修改后的代码如下:

class Check(object):    def __init__(self, name, val=None):        self.name = name        self.val = val    def __get__(self, instance, owner):        print 'Getting', self.name        return self.val    def __set__(self, instance, val):        if val < 0:            raise ValueError('Cannot be negtive')        print 'Updating', self.name        self.val = valclass MathScore(object):    std_id = Check('std_id')    score = Check('score')    def __init__(self, std_id, score):        self.std_id = std_id        self.score = score
原创粉丝点击