杂项之python描述符协议

本节内容

  1. 由来
  2. 描述符协议概念
  3. 类的静态方法及类方法实现原理
  4. 类作为装饰器使用

1. 由来

闲来无事去看了看django中的内置分页方法,发现里面用到了类作为装饰器来使用,由于之前就看到过这一类的用法,但是一直没有明白具体是如何实现的,今天本着打破砂锅问到底的精神去网上搜资料,在这里不得不吐槽下百度搜索的垃圾了。。。。。竞价排名做的那么6,搜一些技术文档。。。。。各种坑爹。。。就是找不到想要的资源。。。
于是翻墙上google搜了搜,找到了python官网的文档。。。里面详细解释了原理以及用法,表示英语渣是硬伤。。。。囫囵吞枣的理解了里面的一部分内容。。。于是就有了本文的诞生。。。。。python官网文档连接在这:
python官方文档地址
英语比较好的小伙伴可以去拜读下。

2. 描述符协议概念

在python中有个概念叫做描述符(descriptors)
描述符在python中是一个协议,协议的定义如下:
如果为某个对象定义了__get__,__set__及__delete__中的任意一种方法,那么就称这个对象是描述符。
描述符是一个功能强大的通用协议,类中的属性,方法,静态方法,类方法甚至super()背后都是基于描述符它是在python2.2中引入的新式类中实现的,描述符使底层的C代码实现更简单,并且为python编程提供了一个灵活的新工具。

官方说明如下:

descr.__get__(self, obj, type=None) --> value

descr.__set__(self, obj, value) --> None

descr.__delete__(self, obj) --> None

如果对象中定义了__get__()和__set__(),它被认为是一个数据描述符。只定义__get__()的描述符称为非数据描述符(它们通常用于方法,但其他用途也是可能的)。
数据和非数据描述符的区别在如何重写对象的字典计算实例。

  • 如果实例字典具有与数据描述符相同的项,则数据描述符将优先。
  • 如果实例字典具有与非数据描述符同名的项,则字典条目优先。

描述符的调用:
描述符在被调用时区别在于调用描述符的是对象还是类:
对于对象:

  • 数据描述符被优先调用
  • 非数据描述符可能会被实例字典所覆盖

解释:
非数据描述符在对象调用中可能会被实例字典覆盖的举例:

#-------------------------------------被实例的字典覆盖的例子-------------------------------------------------------------
class RevealAccess:
"""A data descriptor that sets and returns values
normally and prints a message logging their access.
""" def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name def __get__(self, obj, objtype):
print('Retrieving', self.name)
return self.val class MyClass:
x=RevealAccess(10,'var"x"') m=MyClass()
m.x=3
print(m.x)
# 3
# 这里的非数据描述符被实例中的字典给覆盖了,当调用实例m的属性x的时候,x对应的是数字3 #---------------------------------------数据描述符被优先调用的例子-------------------------------------------------------
class RevealAccess:
"""A data descriptor that sets and returns values
normally and prints a message logging their access.
""" def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name def __get__(self, obj, objtype):
print('Retrieving', self.name)
return self.val def __set__(self, obj, val):
print('Updating', self.name)
print(obj,val)
self.val = val class MyClass:
x=RevealAccess(10,'var"x"') m=MyClass()
m.x=3
print(m.x)
# Updating var"x"
# <__main__.MyClass object at 0x0000021E5F33A7B8> 3
# Retrieving var"x"
# 3 # 这里的数据描述符无法被实例中的字典覆盖。。。当实例m给属性x赋值的时候,实际是调用了x对应的RevealAccess对象的__set__方法给属性x赋值,这里的数据描述符的优先级比对象的属性的优先级更高

会将对象b.x 转换成 type(b).__dict__[‘x’].__get__(obj, type(b))
对于类:
会将class.x 转换成 class.__dict__[‘x’].__get__(None, class)

python模拟__getattribute__()的实现

def __getattribute__(self, key):
"Emulate type_getattro() in Objects/typeobject.c"
v = object.__getattribute__(self, key)
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v

需要重点记住的点:

  • 描述符只有在__getattribute__()的时候才会被调用
  • 重写__get__将会覆盖默认的__getattribute__()中的调用
  • __getattribute__()只适用于新式类和对象
  • object.__getattribute__() 和 type.__getattribute__()调用__get__()的方式不同
  • 数据描述符总是覆盖实例中的字典
  • 非数据描述符可能被实例字典所覆盖

例子:

class RevealAccess:
"""A data descriptor that sets and returns values
normally and prints a message logging their access.
""" def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name def __get__(self, obj, objtype):
print('Retrieving', self.name)
return self.val def __set__(self, obj, val):
print('Updating', self.name)
print(obj,val)
self.val = val class MyClass:
x=RevealAccess(10,'var"x"') m=MyClass()
print(m.x)
# Retrieving var"x" 通过对象调用属性x,将会执行RevealAccess对象的__get__方法(描述符)。
# 10
m.x = 20 # 将20赋值给MyClass的对象m的属性x-->RevealAccess对象-->调用RevealAccess对象的__set__方法
# Updating var"x"
# <__main__.MyClass object at 0x0000013064DAA7F0> 20 # __set__方法接收的第一个参数是MyClass对象,第二个参数是=后面的值
print(m.x)
# Retrieving var"x"
# 20

3. 类的静态方法及类方法实现原理

类的静态方法及类方法作用可以参考我之前的博客:
python 高级之面向对象初级

类的静态方法实现原理:

class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c" def __init__(self, f):
self.f = f def __get__(self, obj, objtype=None):
return self.f class E(object):
@staticmethod
def f(x):
print x
# 上面的装饰器相当于 f = staticmethod(f) print E.f(3)
# 3
print E().f(3)
# 3

类方法实现原理:

class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
def newfunc(*args):
return self.f(klass, *args)
return newfunc class Dict(object): @classmethod
def fromkeys(klass, iterable, value=None):
"Emulate dict_fromkeys() in Objects/dictobject.c"
d = klass()
for key in iterable:
d[key] = value
return d
# 上面的装饰器相当于 fromkeys = classmethod(fromkeys) print(Dict.fromkeys('abracadabra'))
# {'a': None, 'r': None, 'b': None, 'c': None, 'd': None} #执行Dict.fromkeys相当创建了classmethod(fromkeys)对象,并执行了该对象的__get__方法,该方法返回一个新的函数,而后面的('abracadabra')相当于传递参数'abracadabra'执行了这个新函数,返回的是fromkeys的执行结果。

4. 类作为装饰器使用

代码如下:

class MyClassDescriptor:
def __init__(self, func, name=None):
self.func = func
self.name = name or func.__name__
self.__doc__=getattr(func, '__doc__') def __get__(self,instance,cls=None):
if instance is None:
print(instance,cls)
return self
print(instance,cls)
res = instance.__dict__[self.name] = self.func(instance)
return res class MyClass:
@MyClassDescriptor
def foo(self):
print("MyClass foo")
# foo=MyClassDescriptor(foo) m.foo
# <__main__.MyClass object at 0x000001BE9597A828> <class '__main__.MyClass'> # 传递进__get__方法的两个参数,第一个是MyClass对象m,第二个是MyClass类的内存地址,在__get__方法中返回的是foo函数的执行结果,也就打印出来了'MyClass foo'
# MyClass foo
print(MyClass.foo)
# None <class '__main__.MyClass'> # 当是类调用foo时,传递进__get__方法中的第一个参数为None,第二个参数是MyClass类的内存地址
# <__main__.MyClassDescriptor object at 0x000001BE9597A7F0>

杂项之python描述符协议的更多相关文章

  1. Iterator Protocol - Python 描述符协议

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

  2. Descriptor - Python 描述符协议

    描述符(descriptor) descriptor 是一个实现了 __get__. __set__ 和 __delete__ 特殊方法中的一个或多个的. 与 descriptor 有关的几个名词解释 ...

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

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

  4. 一文掌握 Python 的描述符协议

    描述符介绍 描述符本质就是一个新式类,在这个新式类中,至少要实现了__get__(),__set__(),__delete__()中的一个.这也被称为描述符协议. class Myclass(obje ...

  5. python描述符理解

    Python中的描述符是一个相对底层的概念 descriptor Any object which defines the methods get(), set(), or delete(). Whe ...

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

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

  7. python描述符descriptor(一)

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

  8. Python描述符的使用

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

  9. 11.python描述符---类的装饰器---@property

    描述符1.描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()这三个内置方法中的一个,描述符也被称为描述符协议(1):__ ...

随机推荐

  1. 前端网络、JavaScript优化以及开发小技巧

    一.网络优化 YSlow有23条规则,中文可以参考这里.这几十条规则最主要是在做消除或减少不必要的网络延迟,将需要传输的数据压缩至最少. 1)合并压缩CSS.JavaScript.图片,静态资源CDN ...

  2. FREERTOS 手册阅读笔记

    郑重声明,版权所有! 转载需说明. FREERTOS堆栈大小的单位是word,不是byte. 根据处理器架构优化系统的任务优先级不能超过32,If the architecture optimized ...

  3. .NET平台开源项目速览(14)最快的对象映射组件Tiny Mapper

    好久没有写文章,工作甚忙,但每日还是关注.NET领域的开源项目.五一休息,放松了一下之后,今天就给大家介绍一个轻量级的对象映射工具Tiny Mapper:号称是.NET平台最快的对象映射组件.那就一起 ...

  4. Postman - 功能强大的 API 接口请求调试和管理工具

    Postman 是一款功能强大的的 Chrome 应用,可以便捷的调试接口.前端开发人员在开发或者调试 Web 程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的 Fi ...

  5. BPM应用开发解决方案分享

    一.需求分析企业整体管理是一个完整的体系,如果 把这个体系比做一个拼图,企业信息化通过各个业务系统覆盖了一部分业务. 企业通过采购实施通用软件的方式,覆盖了企业的核心业务和专业化业务然而系统只满足了部 ...

  6. <程序员从入门到精通> -- How

    定位 自己才是职业生涯的管理者,想清楚自己的发展路径: 远期的理想是什么?近期的规划是什么?今日的任务和功课又是什么? 今日之任务或功课哪些有助于近期之规划的实现,而近期之规划是否有利于远期之理想? ...

  7. 第14章 Linux启动管理(3)_系统修复模式

    3. 系统修复模式 3.1 单用户模式 (1)在grub界面中选择第2项,并按"e键"进入编辑.并在"-quiet"后面加入" 1",即&q ...

  8. JavaScript知识结构图

    画的一个知识结构图,方便理解.

  9. CYQ.Data V5 分布式缓存Redis应用开发及实现算法原理介绍

    前言: 自从CYQ.Data框架出了数据库读写分离.分布式缓存MemCache.自动缓存等大功能之后,就进入了频繁的细节打磨优化阶段. 从以下的更新列表就可以看出来了,3个月更新了100条次功能: 3 ...

  10. 一步步搭建自己的博客 .NET版(2、评论功能)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...