杂项之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. SQLSERVER将一个文件组的数据移动到另一个文件组

    SQLSERVER将一个文件组的数据移动到另一个文件组 有经验的大侠可以直接忽视这篇文章~ 这个问题有经验的人都知道怎麽做,因为我们公司的数据量不大没有这个需求,也不知道怎麽做实验 今天求助了QQ群里 ...

  2. Javascript - Promise学习笔记

    最近工作轻松了点,想起了以前总是看到的一个单词promise,于是耐心下来学习了一下.   一:Promise是什么?为什么会有这个东西? 首先说明,Promise是为了解决javascript异步编 ...

  3. iOS---iOS10适配iOS当前所有系统的远程推送

    一.iOS推送通知简介 众所周知苹果的推送通知从iOS3开始出现, 每一年都会更新一些新的用法. 譬如iOS7出现的Silent remote notifications(远程静默推送), iOS8出 ...

  4. UWP开发之Template10实践:本地文件与照相机文件操作的MVVM实例(图文付原代码)

    前面[UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理]章节已经提到过Template10,为了认识MvvmLight的区别特做了此实例. 原代码地址:ht ...

  5. 年度巨献-WPF项目开发过程中WPF小知识点汇总(原创+摘抄)

    WPF中Style的使用 Styel在英文中解释为”样式“,在Web开发中,css为层叠样式表,自从.net3.0推出WPF以来,WPF也有样式一说,通过设置样式,使其WPF控件外观更加美化同时减少了 ...

  6. PHP之Memcache缓存详解

         Mem:memory缩写(内存):内存缓存 1.  断电或者重启服务器内存数据即消失,即临时数据: Memcache默认端口:11211 存入方式:key=>>value    ...

  7. Mysql - 函数

    Mysql提供的函数是在是太多了, 很多我都见过, 别说用了. 园子里面, 有人弄了一个比较全的. MYSQL函数 我这里会将他写的完全拷贝下来, 中间会插入一些自己项目中使用过的心得 一.数学函数 ...

  8. Linux下高cpu解决方案

    昨天搞定了一个十万火急的issue,客户抱怨产品升级后系统会变慢和CPU使用率相当高,客户脾气很大,声称不尽快解决这个问题就退货,弄得我们 R&D压力很大,解决这个issue的任务分给了我,客 ...

  9. 08讲browse命令的使用技巧

    .浏览所有parts ,使用技巧 .浏览所有 nets,使用技巧 在上图中选择nets .浏览所有 offpage connector,使用技巧 如上 .浏览所有 DRC makers,使用技巧 5. ...

  10. hibernate-mapping-3.0.dtd;hibernate-configuration-3.0.dtd;hibernate.properties所在路径

    hibernate-mapping-3.0.dtd 所在路径:hibernate-release-5.2.5.Final\project\hibernate-core\src\main\resourc ...