首页 > 编程语言 > 详细

python描述符 descriptor

时间:2017-01-09 23:32:53      阅读:220      评论:0      收藏:0      [点我收藏+]
descriptor
  在python中,如果一个新式类定义了__get__, __set__, __delete__方法中的一个或者多个,那么称之为descriptor。descriptor通常用来改变默认的属性访问(attribute lookup), 前提是descriptor的实例是类的属性(class attribute)。下面的代码展示了简单的用法
 
 1 # -*- coding: utf-8 -*-
 2 class Des(object):
 3     def __init__(self, init_value):
 4         self.value = init_value
 5  
 6     def __get__(self, instance, typ):
 7         print(call __get__, instance, typ)
 8         return self.value
 9  
10     def __set__(self, instance, value):
11         print (call __set__, instance, value)
12         self.value = value
13  
14     def __delete__(self, instance):
15         print (call __delete__, instance)
16  
17 class Widget(object):
18     t = Des(1)
19  
20 def main():
21     w = Widget()
22     print type(w.t)
23     w.t = 1
24     print w.t, Widget.t
25     del w.t
26     print w.t
27  
28  
29 if __name__==__main__:
30     main()

 

这三个特殊的函数签名是这样的:
object.__get__(selfinstanceowner):return value
object.__set__(selfinstancevalue):return None
object.__delete__(selfinstance): return None
 
形参中,instance是类的实例(w), owner是类(widget)
w.t 等价于 Pro.__get__(t, w, Widget).而Widget.t 等价于 Pro.__get__(t, None, Widget)
 
descriptor主要用于控制属性的访问(读、写、删除)。python doc里面有写到,property()就是一个data descriptor实现,__slots__里面的每个属性也是用descriptor实现的.  
__slots__ are implemented at the class level by creating descriptors for each variable name
 
在实践中,我们有可能需要监控或者限制对属性的访问。比如,对象的一个属性被“莫名其妙”地修改了,但搜索所有文件有找不到可以的地方,那么我们可以通过__setattr__(self, k, v)方法,对于我们关心的 k 打印出调用栈。另外,也可以用property,示例代码如下:
 1 class TestProperty(object):
 2     def __init__(self):
 3         self.__a = 1
 4  
 5     @property
 6     def a(self):
 7         return self.__a
 8  
 9     @a.setter
10     def a(self, v):
11         print(output call stack here)
12         self.__a = v
13  
14 if __name__==__main__:
15     t = TestProperty()
16     print t.a
17     t.a = 2
18     print t.a

 

如果需要禁止对属性赋值,或者对新的值做检查,也很容易修改上面的代码实现
 
既然有了property,那什么时候还需要descriptor呢?property最大的问题在于不能重复使用,即对每个属性都需要property装饰,代码重复冗余。而使用descriptor,把相同的逻辑封装到一个单独的类,使用起来方便多了。详细的示例可以参见这篇文章
 
笔者之前看bottle.py源码的时候,看到这么一个descriptor使用,部分源代码和测试代码如下:
 
 1 import functools, time
 2 class cached_property(object):
 3     """ A property that is only computed once per instance and then replaces
 4         itself with an ordinary attribute. Deleting the attribute resets the
 5         property. """
 6 
 7     def __init__(self, func):
 8         functools.update_wrapper(self, func)
 9         self.func = func
10 
11     def __get__(self, obj, cls):
12         if obj is None: return self
13         value = obj.__dict__[self.func.__name__] = self.func(obj)
14         return value
15 
16 class TestClz(object):
17     @cached_property
18     def complex_calc(self):
19         print very complex_calc
20         return sum(range(100))
21 
22 if __name__==__main__:
23     t = TestClz()
24     print >>> first call
25     print t.complex_calc
26     print >>> second call
27     print t.complex_calc

 

运行结果如下:
     >>> first call
    very complex_calc
    4950
    >>> second call
    4950
 
注意两点:
  第一,在访问complex_calc的时候并没有使用函数调用(没有括号);
  第二,第一次调用的时候打印了“very complex_calc”,第二次没有。
 
笔者也是因为这段代码开始学习descriptor,但看懂这段代码还需要了解Python的属性查找顺序,下一篇文章会对此简单介绍。
 
references:
(1)Implementing Descriptors, python2.7 doc
(2)Python描述符(descriptor)解密, http://www.geekfan.net/7862/

 

python描述符 descriptor

原文:http://www.cnblogs.com/xybaby/p/6266686.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!