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

定义

  • 一个描述符是一个有"绑定行为"的对象属性(object attribute),它的访问控制会被描述器协议方法重写。
  • 任何定义了 __get__, __set__ 或者 __delete__ 任一方法的类称为描述符类,其实例对象便是一个描述符,这些方法称为描述符协议。
  • 当对一个实例属性进行访问时,Python 会按 obj.__dict__type(obj).__dict__type(obj)的父类.__dict__ 顺序进行查找,如果查找到目标属性并发现是一个描述符,Python 会调用描述符协议来改变默认的控制行为。
  • 描述符是 @property @classmethod @staticmethodsuper 的底层实现机制。

特性

  • 同时定义了 __get____set__ 的描述符称为 数据描述符(data descriptor);仅定义了 __get__ 的称为 非数据描述符(non-data descriptor) 。两者区别在于:如果 obj.__dict__ 中有与描述符同名的属性,若描述符是数据描述符,则优先调用描述符,若是非数据描述符,则优先使用 obj.__dict__ 中属性。
  • 描述符协议必须定义在类的层次上,否则无法被自动调用。

描述符协议

__get__(self, instance, owner)

_:param self: _描述符对象本身

_:param instance: _使用描述符的对象的实例

_:param owner: _使用描述符的对象拥有者

__set__(self, instance, value)

_:param value: _对描述符的赋值

__delete__(self, instance)

实例

class LazyProperty(object):
"""
实现惰性求值(访问时才计算,并将值缓存)
利用了 obj.__dict__ 优先级高于 non-data descriptor 的特性
第一次调用 __get__ 以同名属性存于实例字典中,之后就不再调用 __get__
"""
def __init__(self, fun):
self.fun = fun def __get__(self, instance, owner):
if instance is None:
return self
value = self.fun(instance)
setattr(instance, self.fun.__name__, value)
return value class ReadonlyNumber(object):
"""
实现只读属性(实例属性初始化后无法被修改)
利用了 data descriptor 优先级高于 obj.__dict__ 的特性
当试图对属性赋值时,总会先调用 __set__ 方法从而抛出异常
"""
def __init__(self, value):
self.value = value def __get__(self, instance, owner):
return self.value def __set__(self, instance, value):
raise AttributeError(
"'%s' is not modifiable" % self.value
) class Circle(object): pi = ReadonlyNumber(3.14) def __init__(self, radius):
self.radius = radius @LazyProperty
def area(self):
print('Computing area')
return self.pi * self.radius ** 2

参考文章

https://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html


欢迎关注

编程思维不应只存留在代码之中,更应伴随于整个人生旅途,这个公众号不只聊技术,还会聊产品/互联网/经济学等广泛话题,所以也欢迎非程序员关注。

Python 描述符(Descriptor) 附实例的更多相关文章

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

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

  2. Python 描述符(descriptor) 杂记

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

  3. python描述符descriptor(一)

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

  4. python描述符 descriptor

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

  5. Python 描述符 (descriptor)

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

  6. Python描述符 (descriptor) 详解

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

  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理解描述符(descriptor)

    Descriptor基础 python中的描述符可以用来定义触发自动执行的代码,它像是一个对象属性操作(访问.赋值.删除)的代理类一样.前面介绍过的property是描述符的一种. 大致流程是这样的: ...

随机推荐

  1. python之 __getattr__、__getattr__、__getitem__、__setitem__ 使用

    python之 __getattr__.__getattr__.__getitem__.__setitem__ 使用 __getattr__内置使用点号获取实例属性属性如 s.name,自调用__ge ...

  2. (转)MySql的常用的语句

    MySQL常用语句 一.连接MySQL 格式: mysql -h 主机地址 -u 用户名 -p 用户密码 1.例1:连接到本机上的MYSQL. 首先在打开DOS窗口,然后进入目录 mysql bin, ...

  3. 07.Javascript——入门高阶函数

    高阶函数英文叫Higher-order function..JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数 ...

  4. appcloud 加载第三方页面loading效果

    apiready = function() { var header = $api.byId('header'); $api.fixIos7Bar(header); var headerPos = $ ...

  5. 洛谷 P2483 [SDOI2010]魔法猪学院

    题目描述 iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练.经过了一周理论知识和一周基本魔法的学习之后,iPig对猪世界的世界本原有了很多的了解:众所周知,世界是由元素构成的:元素与 ...

  6. codeforces 121 E. Lucky Array

    time limit per test 4 seconds memory limit per test 256 megabytes input standard input output standa ...

  7. linux下使用OpenCV的一些问题

    完整正确的代码如下: import cv2 import numpy as np image = cv2.imread('Pictures/a.png') cv2.imshow('original_i ...

  8. js设置元素float的问题

    用js设置一个元素的float样式 div.style.float = 'left'; 这句话在谷歌浏览器或许没问题,但是在IE,火狐下会无效 正确写法是 div.style.styleFloat = ...

  9. QT+动手设计一个登陆窗口+布局

    登陆窗口的样式如下: 这里面涉及着窗口的UI设计,重点是局部布局和整体布局, 首先在ui窗口上添加一个容器类(Widget),然后将需要添加的控件放置在容器中,进行局部布局(在进行局部布局的时候可以使 ...

  10. HITICS || 2018大作业 程序人生 Hello's P2P

    摘  要 本文通过分析一个hello.c的完整的生命周期,从它开始被编译,到被汇编.链接.在进程中运行,讲解了Linux计算机系统执行一个程序的完整过程. 关键词:操作系统,进程,程序的生命周期 目 ...