python描述符 descriptor
descriptor简介
这三个特殊的函数签名是这样的:
object.
__get__
(self, instance, owner):return value
object.
__set__
(self, instance, value):return None
object.
__delete__
(self, instance): return None
# -*- coding: utf-8 -*-
class Des(object):
def __init__(self, init_value):
self.value = init_value def __get__(self, instance, typ):
print('call __get__', instance, typ)
return self.value def __set__(self, instance, value):
print ('call __set__', instance, value)
self.value = value def __delete__(self, instance):
print ('call __delete__', instance) class Widget(object):
t = Des(1) def main():
w = Widget()
print type(w.t)
w.t = 1
print w.t, Widget.t
del w.t if __name__=='__main__':
main()
运行结果如下:
('call __get__', <__main__.Widget object at 0x02868570>, <class '__main__.Widget'>)
<type 'int'>('call __set__', <__main__.Widget object at 0x02868570>, 1)
('call __get__', <__main__.Widget object at 0x02868570>, <class '__main__.Widget'>)
1 ('call __get__', None, <class '__main__.Widget'>)1
('call __delete__', <__main__.Widget object at 0x02868570>)
从输出结果可以看到,对于这个三个特殊函数,形参instance是descriptor实例所在的类的实例(w), 而形参owner就是这个类(widget)
descriptor注意事项
需要注意的是, descriptor的实例一定是类的属性,因此使用的时候需要自行区分实例。比如下面这个例子,我们需要保证以下属性不超过一定的阈值。
class MaxValDes(object):
def __init__(self, inti_val, max_val):
self.value = inti_val
self.max_val = max_val def __get__(self, instance, typ):
return self.value def __set__(self, instance, value):
self.value= min(self.max_val, value) class Widget(object):
a = MaxValDes(0, 10) if __name__ == '__main__':
w0 = Widget()
print 'inited w0', w0.a
w0.a = 123
print 'after set w0',w0.a
w1 = Widget()
print 'inited w1', w1.a
代码很简单,我们通过MaxValDes这个descriptor来保证属性的值不超过一定的范围。运行结果如下:
inited w0 0
after set w0 10
inited w1 10
可以看到,对w0.a的赋值符合预期,但是w1.a的值却不是0,而是同w0.a一样。这就是因为,a是类Widget的类属性, Widget的实例并没有'a'这个属性,可以通过__dict__查看。
那么要怎么修改才符合预期呢,看下面的代码:
class MaxValDes(object):
def __init__(self, attr, max_val):
self.attr = attr
self.max_val = max_val def __get__(self, instance, typ):
return instance.__dict__[self.attr] def __set__(self, instance, value):
instance.__dict__[self.attr] = min(self.max_val, value) class Widget(object):
a = MaxValDes('a', 10)
b = MaxValDes('b', 12)
def __init__(self):
self.a = 0
self.b = 1 if __name__ == '__main__':
w0 = Widget()
print 'inited w0', w0.a, w0.b
w0.a = 123
w0.b = 123
print 'after set w0',w0.a, w0.b w1 = Widget()
print 'inited w1', w1.a, w1.b
运行结果如下:
inited w0 0 1
after set w0 10 12
inited w0 0 1
可以看到,运行结果比较符合预期,w0、w1两个实例互不干扰。上面的代码中有两点需要注意:
第一:第7、10行都是通过instance.__dict__来取值、赋值,而不是调用getattr、setattr,否则会递归调用,死循环。
第二:现在类和类的实例都拥有‘a’属性,不过w0.a调用的是类属性‘a',具体原因参见下一篇文章
descriptor应用场景
They are the mechanism behind properties, methods, static methods, class methods, and
super()
. They are used throughout Python itself to implement the new style classes introduced in version 2.2.
class TestProperty(object):
def __init__(self):
self.__a = 1 @property
def a(self):
return self.__a @a.setter
def a(self, v):
print('output call stack here')
self.__a = v if __name__=='__main__':
t = TestProperty()
print t.a
t.a = 2
print t.a
如果需要禁止对属性赋值,或者对新的值做检查,也很容易修改上面的代码实现
既然有了property,那什么时候还需要descriptor呢?property最大的问题在于不能重复使用,即对每个属性都需要property装饰,代码重复冗余。而使用descriptor,把相同的逻辑封装到一个单独的类,使用起来方便多了。详细的示例可以参见这篇文章。
import functools, time
class cached_property(object):
""" A property that is only computed once per instance and then replaces
itself with an ordinary attribute. Deleting the attribute resets the
property. """ def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func def __get__(self, obj, cls):
if obj is None: return self
value = obj.__dict__[self.func.__name__] = self.func(obj)
return value class TestClz(object):
@cached_property
def complex_calc(self):
print 'very complex_calc'
return sum(range(100)) if __name__=='__main__':
t = TestClz()
print '>>> first call'
print t.complex_calc
print '>>> second call'
print t.complex_calc
>>> first callvery complex_calc4950>>> second call4950
第一,在访问complex_calc的时候并没有使用函数调用(没有括号);
references
python描述符 descriptor的更多相关文章
- Python 描述符(descriptor) 杂记
转自:https://blog.tonyseek.com/post/notes-about-python-descriptor/ Python 引入的“描述符”(descriptor)语法特性真的很黄 ...
- python描述符descriptor(一)
Python 描述符是一种创建托管属性的方法.每当一个属性被查询时,一个动作就会发生.这个动作默认是get,set或者delete.不过,有时候某个应用可能会有 更多的需求,需要你设计一些更复杂的动作 ...
- python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解
1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...
- Python描述符 (descriptor) 详解
1.什么是描述符? python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问.这些方法有 __get__(), __set__(), 和__delete__().如 ...
- Python 描述符(Descriptor) 附实例
在 Python 众多原生特性中,描述符可能是最少被自定义的特性之一,但它在底层实现的方法和属性却无时不刻被使用着,它优雅的实现方式体现出 Python 简洁之美. 定义 一个描述符是一个有" ...
- Python 描述符 (descriptor)
1.什么是描述符? 描述符是Python新式类的关键点之一,它为对象属性提供强大的API,你可以认为描述符是表示对象属性的一个代理.当需要属性时,可根据你遇到的情况,通过描述符进行访问他(摘自Pyth ...
- python描述符descriptor(二)
python内置的描述符 python有些内置的描述符对象,property.staticmethod.classmethod,python实现如下: class Property(object): ...
- 【python】描述符descriptor
开始看官方文档,各种看不懂,只看到一句Properties, bound and unbound methods, static methods, and class methods are all ...
- 杂项之python描述符协议
杂项之python描述符协议 本节内容 由来 描述符协议概念 类的静态方法及类方法实现原理 类作为装饰器使用 1. 由来 闲来无事去看了看django中的内置分页方法,发现里面用到了类作为装饰器来使用 ...
随机推荐
- Responder一点也不神秘————iOS用户响应者链完全剖析
一.事件分类 对于IOS设备用户来说,他们操作设备的方式主要有三种:触摸屏幕.晃动设备.通过遥控设施控制设备.对应的事件类型有以下三种: 1.触屏事件(Touch Event) 2.运动事件(Moti ...
- 基于JDK6的JAX-WX为客户端提供XML与JSON格式数据服务,以及客户端采用AXIS调用案例
1:WebService服务端工程目录如下: 需要第三方jar包:gson-2.2.4.jar\javax.xml.bind.jar\commons-lang-2.5.jar 源码如下: packag ...
- How to install phpmyadmin on centos 6
Phpmyadmin : Phpmyadmin is a free tool used to administrate MySQL . Phpmyadmin supports all major ...
- 【转】ubuntu14.04 trusty的源
原文网址:http://blog.chinaunix.net/uid-15041-id-4821715.html 一.编辑更新源文件:/etc/apt/sources.list二.更新源索引文件:ap ...
- NOI 2013 矩阵游戏
http://uoj.ac/problem/124 矩阵乘法. 十进制快速幂. 刚开始还傻傻地写二进制快速幂,然后陈老师一语点醒梦中人...... #include<cstdio> #in ...
- SQL语言的组成
在正式学习SQL语言之前,首先让我们对SQL语言有一个基本认识,介绍一下SQL语言的 组成: 1.一个SQL数据库是表(Table)的集合,它由一个或多个SQL模式定义. 2.一个SQL表由行集构成, ...
- jQuery手机触屏左右滑动切换焦点图特效代码
原文地址:http://www.17sucai.com/pins/4857.html 演示地址:http://www.17sucai.com/pins/demoshow/4857 干净演示地址:htt ...
- socketFunction
socket socket() 我们使用系统调用socket()来获得文件描述符: #include<sys/types.h> #include<sys/socket.h&g ...
- 第34讲 UI组件之 ProgressDialog和Message
第34讲UI组件之 ProgressDialog和Message 1.进度对话框 ProgressDialog <1>简介 ProgressDialog是AlertDialog类的一个扩展 ...
- PHP设计模式笔记五:策略模式 -- Rango韩老师 http://www.imooc.com/learn/236
策略模式 1.概述:策略模式,将一组特定的行为和算法封装成类,以适应某些特定的上下文环境,这种模式称为策略模式 例如:一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有广告位展示不 ...