先看一个例子,@property。被@property修饰的成员函数,将变为一个描述符。这是最简单的创建描述符的方式。

class Foo:
@property
def attr(self):
print('getting attr')
return 'attr value' def bar(self): pass foo = Foo()

上面这个例子中, attr 是类 Foo 的一个成员函数,可通过语句 foo.attr() 被调用。 但当它被 @property 修饰后,这个成员函数将不再是一个函数,而变为一个描述符。 bar 是一个未被修饰的成员函数。 type(Foo.attr)type(Foo.bar) 的结果分别为:

<type 'property'>
<type 'instancemethod'>

attr 的类型为 property (注:一个 property 类型的对象总是一个描述符), bar 的类型为 instancemethod ,也即一个常规的成员函数。

此时 attr 将无法再被调用,当尝试调用它时,语句 foo.attr() 将抛出错误:

TypeError: 'str' object is not callable

让我们来理解这个错误。

首先来看 foo.attr 的值:

attr value

其类型 type(foo.attr)

str

foo.attr 的类型为 str ,因此便有了以上的错误,一个 str 对象无法被调用。其值为'attr value',正好是原始 attr 函数的返回值。 因此语句 foo.attr 实际上触发了原始 attr 函数的调用,并且将函数的返回值作为其值。实际上语句 print(foo.attr) 的输出为:

getting attr
attr value

进一步验证了执行语句 foo.attr 时,原始的 attr 函数被调用。

发生了什么?当执行一个访问对象属性的语句 foo.attr 时,结果一个函数调用被触发!这便是描述符的作用:将属性访问转变为函数调用,并由这个函数来控制这个属性的值(也即函数的返回值),以及在返回值前做定制化的操作。此时可以给描述符一个简要定义:

描述符是类的一个属性,控制类实例对象访问这个属性时如何返回值及做哪些额外操作

这留给程序员的空间是巨大的。。

描述符协议

任何实现了描述符协议的类都可以作为描述符类。描述符协议为一组成员函数定义,包括:

函数 作用 返回值 是否必须
__get__(self, obj, type) 获取属性值 属性的值
__set__(self, obj, value) 设置属性的值 None
__delete__(self, obj) 删除属性 None

如果一个类实现了以上成员函数,则它便是一个描述符类,其实例对象便是一个描述符

下面是一个自定义的描述符的实现。

class MyDescriptor:
def __init__(self):
self.data = None
def __get__(self, obj, type):
print('get called')
return self.data
def __set__(self, obj, value):
print('set called')
self.data = value
def __delete__(self, obj):
print('delete called')
del self.data class Foo:
attr = MyDescriptor() foo = Foo()

示例中 MyDescriptor 实现了描述符协议(也即实现了 __get__, __set__, __delete__ 函数),因此其为一个描述符类。 Fooattr 属性为 MyDescriptor 类的实例对象,因此它是一个描述符。

print(foo.attr) 的输出为:

get called
None

可见当访问 fooattr 属性时, MyDescriptor__get__ 函数被调用。

foo.attr = 'new value' 的输出为:

set called

可见当为 attr 设置一个新值时, MyDescriptor__set__ 函数被调用。

再运行 print(foo.attr) ,输出为:

get called
new value

可见新值已被设置。

del foo.attr 的输出为:

delete called

可见当为删除属性 attr 时, MyDescriptor__delete__ 函数被调用。

再执行 print(foo.attr)AttributeError 被抛出:

get called
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "1.py", line 6, in __get__
return self.data
AttributeError: 'MyDescriptor' object has no attribute 'data'

可见属性 attr 已被删除。

参数意义

__get__(self, obj, type) 函数各个参数的意义为:

参数 意义 例子中的对应
self 描述符对象本身 Foo.attr
obj 使用描述符的对象实例 foo
type obj的类型 Foo

__set__(self, obj, value) 函数的self和obj参数的意义同 __get__ ,value的意义为:

参数 意义 例子中的对应
value 属性的新值 'new value'

__delete__(self, obj) 函数的self和obj参数的意义同 __get__

(全文完)

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. 【一天一道LeetCode】#30. Substring with Concatenation of All Words

    注:这道题之前跳过了,现在补回来 一天一道LeetCode系列 (一)题目 You are given a string, s, and a list of words, words, that ar ...

  2. 一个 redis 异常访问引发 oom 的案例分析

    「推断的前提是以事实为依据.」 这两天碰到一个线上系统的偶尔出现突然堆内存暴涨,这倒不是个什么疑难杂症, 只是过程中有些思路觉得可以借鉴参考,故总结下并写下来. 现象 内存情况可以看看下面这张监控图. ...

  3. [转]C# 之DLL调用(托管与非托管)

    每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍.首先,您需要了解什么是托管,什么是非托管.一般可以认为:非托管代码主要是基于win 32平台开发的DLL,activeX ...

  4. volley请求原理

    Volley 实现原理解析 本文为 Android 开源项目实现原理解析 中 Volley 部分 项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo 分析者:g ...

  5. 【Qt编程】Qt学习之状态栏

    QStatusBar类提供一个水平条来显示状态信息.所谓状态信息,拿个简单的例子来说,当你在word中编辑时,左下角就会出现页面.字数等等信息.状态信息可以分为三类:临时信息,如一般的提示信息:正常信 ...

  6. 基于GraphCuts图割算法的图像分割----OpenCV代码与实现

    转载请注明出处:http://blog.csdn.net/wangyaninglm/article/details/44151213, 来自:shiter编写程序的艺术 1.绪论 图切割算法是组合图论 ...

  7. LIRe 源代码分析 6:检索(ImageSearcher)[以颜色布局为例]

    ===================================================== LIRe源代码分析系列文章列表: LIRe 源代码分析 1:整体结构 LIRe 源代码分析 ...

  8. Boyer-Moore算法

    1.概述 在用于查找子字符串的算法当中,BM(Boyer-Moore)算法是目前相当有效又容易理解的一种,一般情况下,比KMP算法快3-5倍. BM算法在移动模式串的时候是从左到右,而进行比较的时候是 ...

  9. VS2010 / MFC + OpenCV 2.4.9打开图片

    原文地址:http://www.opencv.org.cn/forum.php?mod=viewthread&tid=30832 第一部分,参考http://jingyan.baidu.com ...

  10. StickyListHeaders的使用

    我们知道在ios中字母的导航有悬停的效果,在android中,git上有大神实现了这种悬停的功能,我们只要将普通的Listview改为StickyListHeadersListView然后设置adap ...