本文主要介绍描述符的定义,个人的一些理解;什么是数据描述符;什么是非数据描述符;描述符的检测等。希望看完这篇文章后,你对描述符有了更清晰的认识。知道怎么判断一个对象是不是描述符,知道如果定义一个描述符,知道什么是该用描述符。当然,最大的目的是,通过学习描述符,让你对python这门语言有更多深入的认识。

什么是描述符

官方的定义:描述符是一种具有“捆绑行为”的对象属性。访问(获取、设置和删除)它的属性时,实际是调用特殊的方法(get(),set(),delete())。也就是说,如果一个对象定义了这三种方法的任何一种,它就是一个描述符。

更多的理解

通常情况下,访问一个对象的搜索链是怎样的?比如a.x,首先,应该是查询 a.dict[‘x’],然后是type(a).dict[‘x’],一直向上知道元类那层止(不包括元类)。如果这个属性是一个描述符呢?那python就会“拦截”这个搜索链,取而代之调用描述符方法(get)。

三个方法(协议):

get(self, instance, owner) —获取属性时调用,返回设置的属性值,通常是set中的value,或者附加的其他组合值。

set(self, instance, value) — 设置属性时调用,返回None.

delete(self, instance) — 删除属性时调用,返回None

其中,instance是这个描述符属性所在的类的实体,而owner是描述符所在的类。

数据描述符(data descriptor)和非数据描述符(non-data descriptors)

数据描述符:定义了setget方法的对象

非数据描述符:只定义了get方法的对象。通常方法都是非数据描述符。比如后面会谈到的staticmethod,classmethod等。

区别:

1、位于搜索链上的顺序。搜索链(或者优先链)的顺序大概是这样的:数据描述符>实体属性(存储在实体的_dict_中)>非数据描述符。

这个顺序初看起来挺晦涩。解释如下:

获取一个属性的时候:

首先,看这个属性是不是一个数据描述符,如果是,就直接执行描述符的get,并返回值。

其次,如果这个属性不是数据描述符,那就按常规去从dict里面取。

最后,如果dict里面还没有,但这是一个非数据描述符,则执行非数据描述符的get方法,并返回。

举例如下:

class NoDataDesc(object):
def __get__(self, instance, owner):
print "nodatadesc.attr"
return instance.freq*2
class DataDesc(object):
def __get__(self, instance, owner):
print "datadesc.attr"
return instance.freq*4
def __set__(self, instance, value):
print "set:datadesc.attr"
instance.doc_freq = instance.freq*value
def __delete__(self, instance):
print "del datadesc.attr"
raise BaseException
class Test(object):
data_result = DataDesc()
nodata_result = DataDesc()
def __init__(self,freq,data,nodata):
self.freq = freq
self.data_result = data
self.nodata_result = nodata test = Test(3,-1,-1)
print test.nodata_result
#print test.data_result12345678910111213141516171819202122232425

注释print test.data_result的时候打印的是:

set:datadesc.attr

-1(这个值是实体的值,相当于打印test.dict[” nodata_result “])

“set:datadesc.attr”是

self.data_result = data

时打印的。

注释print test.nodata_result的输出:

set:datadesc.attr

datadesc.attr

12(这个是描述符set中设置的值。相当于data_result.get(test,3),返回的是12)

谈到这个搜索链的顺序,稍微再引申一下。问一个问题:

如果Test这个类还有getattr,getattribute 呢?

这里就不多介绍getattr,getattribute (每一个都可以另外再起一篇文章了)。熟悉的记住一点就行:

getattribute优先级是最高的,而getattr是最低的。

如何检测一个对象是不是描述符

如果把问题换成——一个对象要满足什么条件,它才是描述符呢——那是不是回答就非常简单了?

答:只要定义了(set,get,delete)方法中的任意一种或几种,它就是个描述符。

那么,继续问:怎么判断一个对象定义了这三种方法呢?

立马有人可能就会回答:你是不是傻啊?看一下不就看出来了。。。

问题是,你看不到的时候呢?python内置的staticmethod,classmethod怎么看?

正确的回答应该是:看这个对象的dict

写到这里,我得先停一下,来解释一个问题。不知道读者有没有发现,上面我一直再说“对象”,“对象”,而实际上指的明明是一个类啊?博主是不是搞错了呢?嗯,作为一名入门语言是java(抛开只玩过,没写过软件的C)的程序猿,把类称之为对象,的确让我本人也有点别扭。但是,在python中,这样称呼又是妥当的。因为,“一切皆对象”,类,不过也是元类的一种对象而已。

跑了下题,接着说。要看对象的dict好办,直接dir(对象)就行了。现在可以写出检测对象是不是描述符的方法了:

def has_descriptor_attrs(obj):
return set(['__get__', '__set__', '__delete__']).intersection(dir(obj)) def is_descriptor(obj):
"""obj can be instance of descriptor or the descriptor class"""
return bool(has_descriptor_attrs(obj)) def has_data_descriptor_attrs(obj):
return set(['__set__', '__delete__']) & set(dir(obj)) def is_data_descriptor(obj):
return bool(has_data_descriptor_attrs(obj))
12345678910111213

测试一下:

print is_descriptor(classmethod), is_data_descriptor(classmethod)
print is_descriptor(staticmethod), is_data_descriptor(staticmethod)
print is_data_descriptor(property)123

输出:

(True, False)

(True, False)

True

看来,特性(property)是数据描述符

描述符有什么用和好处

1)一般情况下不会用到,建议:先定基本的,以后真有需要再扩展。别贪玩。

2)可以在设置属性时,做些检测等方面的处理

3)缓存?

4)设置属性不能被删除?那定义delete方法,并raise 异常。

5)还可以设置只读属性

6)把一个描述符作为某个对象的属性。这个属性要更改,比如增加判断,或变得更复杂的时候,所有的处理只要在描述符中操作就行了。

老实说,博主也想不出太多描述符使用的场景,举不出什么例子来。希望有人能帮忙补充。不甚感激。

例子

1、一个学校类,学校类又一个学生属性,赋给这个属性的值是一个json格式的字符串。需要json解析。现在是要处理学校很多学生的数据。要求:不能因为某个学生属性有问题而导致其他学生的数据有问题。并且这个学生属性所在这条记录要保存,对于它的属性,置空。

代码如下:

class Student(object):
def __init__(self):
self.inst = {}
def __get__(self, instance, owner):
return self.inst[instance] def __set__(self, instance, value):
try:
self.inst[instance] = json.loads(value)
except ValueError,e:
print "error"
self.inst[instance] = "" def __delete__(self, instance):
raise BaseException class School(object):
student = Student()
def __init__(self,tid,title,student):
self.tid = tid
self.name = title
self.student = student def main():
lst = []
for i in xrange(10):
student = '{"aturbo":{"english":"100","math":"100"}}'
if i ==1:
student = '{"sansan":{"english":"40","scid":"20"}}'
if i == 3:
#print i
student ='{"sansan"}'
scl = School(str(i),"fege",student)
lst.append(scl) for info in lst:
print info.tid,':',info.student if __name__ =="__main__":
main()12345678910111213141516171819202122232425262728293031323334353637383940

打印结果:

0 : {u’aturbo’: {u’math’: u’100’, u’english’: u’100’}}

1 : {u’sansan’: {u’scid’: u’20’, u’english’: u’40’}}

2 : {u’aturbo’: {u’math’: u’100’, u’english’: u’100’}}

3 :

4 : {u’aturbo’: {u’math’: u’100’, u’english’: u’100’}}

5 : {u’aturbo’: {u’math’: u’100’, u’english’: u’100’}}

6 : {u’aturbo’: {u’math’: u’100’, u’english’: u’100’}}

7 : {u’aturbo’: {u’math’: u’100’, u’english’: u’100’}}

8 : {u’aturbo’: {u’math’: u’100’, u’english’: u’100’}}

9 : {u’aturbo’: {u’math’: u’100’, u’english’: u’100’}}

注意两点:

1)tid为1这条的取值,tid=3这条的取值

2)这样定义Student这个描述符也可以:

class Student(object):
def __get__(self, instance, owner):
return instance.value def __set__(self, instance, value):
try:
instance.value = json.loads(value)
except ValueError,e:
print "error"
instance.value = "" def __delete__(self, instance):
raise BaseException12345678910111213

这样呢:

    class Student(object):
def __get__(self, instance, owner):
return self.value def __set__(self, instance, value):
try:
self.value = json.loads(value)
except ValueError, e:
print "error"
self.value = ""12345678910

也正确,但student变成类成员,不会是实体成员,打印的结果会全部一样(都是:{u’aturbo’: {u’math’: u’100’, u’english’: u’100’}}

总结

你可能还从没有在实际工作中写过一个描述符,甚至觉得今后也极少有机会会用的。但是,不可避免地,你每天(只要是在写python代码)都或多或少的要和描述符打交道。要非常好的理解描述符并不是一件容易的事,希望这篇这篇文章能够在一定程度上帮到你。相信我,好好理解下描述符会让你对python的认识提高不少。

python——描述符的更多相关文章

  1. 杂项之python描述符协议

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

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

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

  3. 【转载】Python 描述符简介

    来源:Alex Starostin 链接:www.ibm.com/developerworks/cn/opensource/os-pythondescriptors/ 关于Python@修饰符的文章可 ...

  4. python描述符descriptor(一)

    Python 描述符是一种创建托管属性的方法.每当一个属性被查询时,一个动作就会发生.这个动作默认是get,set或者delete.不过,有时候某个应用可能会有 更多的需求,需要你设计一些更复杂的动作 ...

  5. python描述符 descriptor

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

  6. Python描述符的使用

    Python描述符的使用 前言 作为一位python的使用者,你可能使用python有一段时间了,但是对于python中的描述符却未必使用过,接下来是对描述符使用的介绍 场景介绍 为了引入描述符的使用 ...

  7. Python描述符 (descriptor) 详解

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

  8. python描述符和属性查找

    python描述符 定义 一般说来,描述符是一种访问对象属性时候的绑定行为,如果这个对象属性定义了__get__(),__set__(), and __delete__()一种或者几种,那么就称之为描 ...

  9. Iterator Protocol - Python 描述符协议

    Iterator Protocol - Python 描述符协议 先看几个有关概念, iterator 迭代器, 一个实现了无参数的 __next__ 方法, 并返回 '序列'中下一个元素,在没有更多 ...

  10. Python描述符以及Property方法的实现原理

    Python描述符以及Property方法的实现原理 描述符的定义: 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实了__get__(),__set__(),__delete__()中 ...

随机推荐

  1. UML和模式应用5:细化阶段(10)---UML交互图

    1.前言 UML使用交互图来描述对象间消息的交互 交互图可以用于动态对象建模. 交互图有两种类型:顺序图和通信图. UML交互图将用来解释和阐述对象设计. 2.顺序图和通信图 顺序图具有丰富的符号标记 ...

  2. Ubuntu 分辨率显示出错,分辨率不是最佳分辨率的解决办法

    本文为转载,但在Ubuntu16.04LTS下亲测有效. (1)首先使用 xrandr 命令列出当前所能检测到的分辨率,如一台显示器,最佳分辨率为 1440x900(我的显示器尽量设置1680x105 ...

  3. robotium之无name、ID仅有desc定位

    场景如图: 没有name和ID,群里问了,也没人搭理我,自己试验了下,发现这个法子可用,直接贴代码: Activity act = solo.getCurrentActivity(); int ide ...

  4. JavaScript中unicode编码与String互转(三种方法)

    1.引言 JS本身就支持unicode转string功能,一共有三种方式和String单个字符转unicode编码. 2.方法 //unicode转String 1. eval("'&quo ...

  5. webpack相关笔记

    文档地址:链接 文档:https://doc.webpack-china.org/configuration/ 慕课网地址:链接 参考地址:链接 1.webpack是一个前端打包工具,特点:代码分割( ...

  6. vue scoped 穿透_vue 修改内部组件样式问题

    何为scoped? 在vue文件中的style标签上,有一个特殊的属性:scoped.当一个style标签拥有scoped属性时,它的CSS样式就只能作用于当前的组件,也就是说,该样式只能适用于当前组 ...

  7. Laravel firstOrNew 与 firstOrCreate 的区别

    例如: $item = App\Deployment::firstOrNew( ['name' => '问答小程序'], ['delayed' => 1] ); firstOrNew 需要 ...

  8. uva12436 回头再做一次

    线段树维护等差数列,结点维护首项+公差即可 #include <cstdio> #include <cstring> #include <algorithm> us ...

  9. this和super不能同时出现在构造方法中

    package com.bjpowernode.t02inheritance.c09; /* * 使用super调用父类的构造方法 */public class TestSuper02 { publi ...

  10. thinkphp注册验证

    在model中新建一个UserModel //覆盖原本的设置 //一次性获得全部验证错误 protected $patchValidate = true; //实现表单项目验证 //通过重写父类属性_ ...