Python 描述符是一种创建托管属性的方法。每当一个属性被查询时,一个动作就会发生。这个动作默认是get,set或者delete。不过,有时候某个应用可能会有

更多的需求,需要你设计一些更复杂的动作。最好的解决方案就是编写一个执行符合需求的动作的函数,然后指定它在属性被访问时运行。一个具有这种功能的对象

称为描述符。描述符是python方法,绑定方法,super,property,staticmethod和classmethod的实现基础。

1.描述符协议

描述符descriptor就是一个表示属性值的对象,通过实现一个或多个__get__,__set__,__delete__方法,可以将描述符与属性访问机制挂钩,还可以自定义这些操作。

__get__(self,instance,own):用于访问属性,返回属性的值。instance为使用描述符的实例对象,own为实例所属的类。当通过类访问属性时,instance为None。

__set__(self,instance,value):设定属性值。

__delete__(self,instance):删除属性值。

2.描述符如何实现

class Descriptor(object):
def __get__(self, instance, owner):
print 'getting:%s'%self._name
return self._name
def __set__(self, instance, name):
print 'setting:%s'%name
self._name = name
def __delete__(self, instance):
print 'deleting:%s'%self._name
del self._name
class Person(object):
name = Descriptor()

一个很简单的描述符对象就产生了,现在可以对一个Person对象进行属性name的读取,设置和删除:

>>> p=Person()
>>> p.name='john'
setting:john
>>> p.name
getting:john
'john'
>>> del p.name
deleting:john

注意:描述符只能在类级别上进行实例化,不能通过在__init__()和其他方法中创建描述符对象来为每个实例创建描述符。

具有描述符的类使用的属性名称比实例上存储的属性名称具有更高的优先级。为了能让描述符在实例上存储值,描述符必须挑选一个与它本身所用名称不同的名称。

如上例,Person类初始化__init__函数为实例设置属性就不能用name名称了。

data描述符与none-data描述符:

如果实现了__get__和__set__就是一个data描述符,如果只有__get__就是一个non-data描述符。不同的效果在于data描述符总是替代在一个实例中的属性实现,

而non-data描述符由于没有set,在通过实例对属性赋值时,例如上面的p.name = 'hello',不会再调用__set__方法,会直接把实例属性p.name设为'hello'。

当然如果仅仅在__set__中raise AttributeError,仍然得到的是一个non-data的描述符。

描述符调用机制:

当查询一个对象的属性a.attr时,如果python发现attr是个描述符对象,如何读取属性取决于对象a:

直接调用:最简单的调用是直接使用代码调用描述符的方法,attr.__get__(a)

实例绑定:如果a是个实例对象,调用方法:type(a).__dict__['attr'].__get__(a,type(a))

类绑定:如果A是个类对象,调用方法:A.__dict__['attr'].__get__(None,A)

super绑定:如果a是个super实例,那么super(B,obj).m()通过查询obj.__class__.__mro__找到B的基类A,然后执行A.__dict__['m'].__get__(obj,obj.__class__)

3.执行属性类型检查的描述符

class TypedProperty(object):
def __init__(self,name,attr_type,default=None):
self.name='_'+name
self.type=attr_type
self.default=default if default else attr_type()
def __get__(self,instance,own):
return getattr(instance,self.name,self.default)
def __set__(self,instance,value):
if not isinstance(value,self.type):
raise TypeError,'Must be %s'%self.type
setattr(instance,self.name,value)
def __delete__(self,instance):
raise AttributeError('Can not delete attribute')
class Foo(object):
name=TypedProperty('name',str)
num=TypedProperty('num',int,37)

上述描述符可以对属性的类型进行检查,如果name属性不设为str类型或者num不设为int类型,就会报错:

>>> f.name=21
TypeError: Must be <type 'str'>

而且禁止对属性进行删除操作:

>>> del f.name
AttributeError: Can not delete attribute

f.name 隐形的调用type(f).__dict__['name'].__get__(f,Foo),即Foo.name.__get__(f,Foo)。

上述描述符实际是存储在实例上的,name通过setattr(f,_name,value)存储在f._name上,num存储在f._num上,这也是加下划线的原因,

否则描述符名称name会和实例属性name发生冲突,描述符属性f.name会覆盖掉实例属性f.name。

python描述符descriptor(一)的更多相关文章

  1. Python 描述符(descriptor) 杂记

    转自:https://blog.tonyseek.com/post/notes-about-python-descriptor/ Python 引入的“描述符”(descriptor)语法特性真的很黄 ...

  2. python描述符 descriptor

    descriptor 在python中,如果一个新式类定义了__get__, __set__, __delete__方法中的一个或者多个,那么称之为descriptor.descriptor通常用来改 ...

  3. python描述符(descriptor)、属性(property)、函数(类)装饰器(decorator )原理实例详解

     1.前言 Python的描述符是接触到Python核心编程中一个比较难以理解的内容,自己在学习的过程中也遇到过很多的疑惑,通过google和阅读源码,现将自己的理解和心得记录下来,也为正在为了该问题 ...

  4. Python描述符 (descriptor) 详解

    1.什么是描述符? python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问.这些方法有 __get__(), __set__(), 和__delete__().如 ...

  5. Python 描述符(Descriptor) 附实例

    在 Python 众多原生特性中,描述符可能是最少被自定义的特性之一,但它在底层实现的方法和属性却无时不刻被使用着,它优雅的实现方式体现出 Python 简洁之美. 定义 一个描述符是一个有" ...

  6. Python 描述符 (descriptor)

    1.什么是描述符? 描述符是Python新式类的关键点之一,它为对象属性提供强大的API,你可以认为描述符是表示对象属性的一个代理.当需要属性时,可根据你遇到的情况,通过描述符进行访问他(摘自Pyth ...

  7. python描述符descriptor(二)

    python内置的描述符 python有些内置的描述符对象,property.staticmethod.classmethod,python实现如下: class Property(object): ...

  8. 【python】描述符descriptor

    开始看官方文档,各种看不懂,只看到一句Properties, bound and unbound methods, static methods, and class methods are all ...

  9. 杂项之python描述符协议

    杂项之python描述符协议 本节内容 由来 描述符协议概念 类的静态方法及类方法实现原理 类作为装饰器使用 1. 由来 闲来无事去看了看django中的内置分页方法,发现里面用到了类作为装饰器来使用 ...

随机推荐

  1. [JavaScript]'this'详解

    http://blog.csdn.net/sodino/article/details/51318565

  2. 编写程序,查找并删除forward_list<int>中的奇数元素

    #include<iostream> #include<forward_list> using namespace std; int main() { forward_list ...

  3. Android4.4 + WebAPI 实现拍照上传

    网上有很多关于拍照上传的实现方法,如果用新版本android去运行有可能会发现根本实现不了.主要原因是android从4.4版本开始通过intent.ACTION_GET_CONTENT打开选择器后, ...

  4. felx第二天 ActionScript 基本语法和关键字

    flex中使用的注解有两种分别是:// 和/**/ flex使用的关键字如图

  5. debian 颜色设置

    root@debian:/# vi /root/.bashrc #export PS1='\h:\w\$ 'export PS1='\[\033[1;32;40m\]\u@\h:\w\$ \[\033 ...

  6. [置顶] operator overloading(操作符重载,运算符重载)运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy)

    operator overloading(操作符重载,运算符重载) 所谓重载就是重新赋予新的意义,之前我们已经学过函数重载,函数重载的要求是函数名相同,函数的参数列表不同(个数或者参数类型).操作符重 ...

  7. Java虚拟机专题

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/5160083. ...

  8. Volatile变量

    关于volatile变量的使用,由于使用得比较多,后面如果需要温习的话可以参考:http://www.infoq.com/cn/articles/java-memory-model-4

  9. Oracle报错:ORA-01747: user.table.column, table.column 或列说明无效

    1.检查sql书写正确性 2.如果sql书写正确,则是由于数据库列名起的不好引起的,名字用到了数据库的关键字. 如果列很多,又不好确定是哪个列名使用了关键字,以下建议可供参考: 我用以下方法定位 se ...

  10. js 简易判断一个数字是否是小数

    参考:js验证输入是否是小数 我的方法是: //check the number is decimal number function checkDecimal(number){ if(!isNaN( ...