Python的描述符乍眼看去简单,但是细节方面如果不注意容易掉坑,总结以下几个坑,以作备忘,先看代码:

class D:
def __get__(self, inst, owner):
if inst is None:
return self
else:
print('I am in the D.__get__')
return inst.__dict__['d'] # 返回实例的d属性
def __set__(self, inst, value):
if not isinstance(value, str):
raise AttributeError('Value must be str')
print('I am in the D.__set__')
inst.__dict__['d'] = value # 设置实例自身d属性,只能使用__dict__形式,否则又会调用__get__,从而陷入无限循环 class C:
d = D()
def __init__(self, value):
self.d = value # 在初始化的时候设置d属性,注意此时调用__get__函数,并不是设置实例本身的属性 >>> c = C('shy')
I am in the D.__set__
>>> c.__dict__
{'d': 'shy'}
>>> c.d
I am in the D.__get__
'shy'

总结:

  1. 描述符只能做类属性,不能作为实例属性,当一个属性是描述符时,实例查找这个属性会直接在类里面查找而忽略实例自身的空间,如上,实例自身有同名的d属性,但是当通过c.d调用的时候,调用的是描述符d。
  2. 在描述符里获取或者设置实例的同名属性时,需要用inst.__dict__形式访问,上面如果写成inst.d,则会陷入无限循环。

再来看一段代码:

class D:
def __get__(self, inst, owner):
if inst is None:
return self
else:
print('I am in D.__get__')
return self.value
def __set__(self, inst, value):
print('I am in D.__set__')
self.value = value class C:
d = D() >>> c1 = C()
>>> c2 = C()
>>> c1.d = 2
I am in D.__set__
>>> c2.d = 3
I am in D.__set__
>>> c1.d
I am in D.__get__
3
>>> c2.d
I am in D.__get__
3 class D:
def __get__(self, inst, owner):
if inst is None:
return self
else:
print('I am in D.__get__')
return inst.value
def __set__(self, inst, value):
print('I am in D.__set__')
inst.value = value class C:
d = D() >>> c1 = C()
>>> c2 = C()
>>> c1.d = 2
I am in D.__set__
>>> c2.d = 3
I am in D.__set__
>>> c1.d
I am in D.__get__
2
>>> c2.d
I am in D.__get__
3

总结:

属性可以保存在描述符内部,也可以保存在实例,但是如果保存为描述符内部,则为所有实例共享,所以一般把描述符的状态的信息保存在描述符内部,而把实例相关的信息保存在实例侧。

2018年12月27日

之前的一个错误,并非只要访问描述符就一定忽略其实例查找,如果只设置了__get__,优先实例查找,如果只设置了__set__函数,访问属性优先在实例字典里面查找,设置属性仍然优先类里面查找,代码如下:

# 只设置__get__,实例设置同名属性,未触发__get__
>>> class D:
def __get__(self, inst, cls):
print('I am in the D.__get__')
return inst.name >>> class C:
name = D()
def __init__(self, value):
self.name = value >>> c = C(2)
>>> c.name
2 #只设置__set__,实例设置同名属性,获取属性时优先实例查找,设置属性时优先类查找
>>> class D:
def __set__(self, inst, value):
print('I am in D __set__')
inst.__dict__['name'] = value >>> class C:
name = D() >>> c = C()
>>> c.name = 2
I am in D __set__
>>> c.__dict__
{'name': 2}
>>> c.name
2
>>> c.name = 3
I am in D __set__

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) 详解

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

  5. python描述符学习

    目录 一.对象属性的访问控制 二.描述符基本理解 三.基本使用 四.使用描述符完成property.classmethod.staticmethod自定义实现 1.property的自定义实现 2.c ...

  6. python描述符descriptor(一)

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

  7. python描述符 descriptor

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

  8. USB学习小记-HID类键盘的报告描述符的理解

    前言 断断续续的学习了将近三个月,才把USB的HID类搞明白,速度真是够慢的.利用晚上+周末的时间学习自己的东西确实是必要的,不过效率是有点低,以后要更专注一些才行,希望自己能做到吧. 在学习过程中, ...

  9. Python描述符的使用

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

随机推荐

  1. GridView导出成Excel字符"0"丢失/数字丢失的处理方式 收藏

    GridView导出成Excel字符"0"丢失/数字丢失的处理方式 收藏 GridView 导出成Excel文件,这个代码在网上比较多.但是发现存在一个问题,导出的数据中如果有&q ...

  2. 「小程序JAVA实战」运行微信官方demo(四)

    转自:https://idig8.com/2018/08/09/xiaochengxu-chuji-04/ 微信官方小程序的demo 介绍 https://mp.weixin.qq.com/cgi-b ...

  3. 由ffmpeg中avformat模块中的 URL_SCHEME_CHARS 看 strspn( ) 的妙用

    在ffmpeg的avformat 模块中avio.c 对 URL_SCHEME_CHARS 的定义: #define URL_SCHEME_CHARS \ "abcdefghijklmnop ...

  4. Java两种延时——thread和timer

    在Java中有时候需要使程序暂停一点时间,称为延时.普通延时用Thread.sleep(int)方法,这很简单.它将当前线程挂起指定的毫秒数.如 try { Thread.currentThread( ...

  5. Vulkan Tutorial 03 理解Instance

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Creating an instance 与Vulkan打交道,通常的步骤是创建一个 ...

  6. SpringBoot18 Swagger、API接口文档生成、WireMock、模拟后台数据

    1 Swagger 1.1 简述 前后端分离的项目需要前后端开发人员协同工作,后台开发人员需要给到前端开发者一套API文档:利用Swagger可以简单高效的帮助后台开发者生成RestfulAPI开发文 ...

  7. [转]asp.net使用uploadify上传出现的IO Error问题

    原文链接:http://blog.csdn.net/w3031213101/article/details/6335878 解决方法:1.uploadify控件的自定义size必须调整大小,即属性:s ...

  8. Django框架 之 Ajax

    Django框架 之 Ajax 浏览目录 AJAX准备知识 AJAX与XML的比较 AJAX简介 jQuery实现的ajax AJAX参数 AJAX请求如何设置csrf_token 序列化 一.AJA ...

  9. Ubuntu下成功安装台式机网卡realtek的rtl8188eu芯片驱动并实现AP功能

    1,下载驱动 https://github.com/lwfinger/rtl8188eu 使用注意: https://github.com/lwfinger/rtl8188eu/issues/3 2. ...

  10. (转)通过Javascript得到URL中的参数(query string)

    原文地址:http://www.cnblogs.com/season-huang/p/3322561.html 我们知道,"GET"请求中,通常把参数放在URL后面,比如这样htt ...