python 中关于descriptor的一些知识问题
这个问题从早上日常扫segmentfault上问题开始
有个问题是
- class C(object):
- @classmethod
- def m():
- pass
- m()是类方法,调用代码如下:
- C.m()
- 但我想当成属性的方式调用,像这样:
- C.m
- 请问该怎么弄呢? 请最好提供个简单的例子, 多谢!
这里我开始误会了他的意思,以为他是想直接使用C().m调用这个方法,如果是这样,直接将装饰器@classmathod改成@property就可以达到效果了。
但是这里他想要达到的效果是C.m 也就是说在不实例化C对象的情况下去调用m方法 有点类似即使用classmathod然后在这个基础上又调用property方法
我想了很久,没有在以前的编码中遇到过,所以来了兴致。
在广泛查找资料之后,我发现跟一个叫descriptor protocol的概念有关。
什么是descriptor?官方文档给的简介是:
In general, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the descriptor protocol. Those methods are
__get__()
,__set__()
, and__delete__()
. If any of those methods are defined for an object, it is said to be a descriptor
通常来说,一个descriptor就是一个绑定行为的对象,一个对属性的访问方法会被描述协议中的方法覆盖。那些方法包括__get__(), __set__()和__delete__().如果任何上面的那些方法出现在了你定义的object中,那么我们就说他是一个descriptor。
换句话来说,如果你定义一个class方法,然后class方法中包含了__get__,__set__和__delete__方法中的任何一个,那么这个类就是一个descriptor。
相信绝大多数人都是用过上面提到的property方法,但是不知道有没有盆友仔细看过property的源码,其实property就是一个典型的descriptor。
说这么多也不明白来复现一下上面的问题怎么解决
- class ClassPro(object):
- def __init__(self, function):
- self.run = function
- def __get__(self, instance, owner):
- return self.run(owner)
- class a(object):
- def pp(self):
- print 'say something'
- pp = ClassPro(pp)
- a.pp
output:- say something
可以看到上面我实现了一个自定义的新式类ClassPro用来装饰下面a类里面的函数pp 。CP实现了__get__方法所以他是一个descriptor,descriptor一旦实现,会覆盖掉原有的从__dict__里面寻找属性的方式。这里我引用别人博客里面翻译过来的doc
通常对一个实例的属性的访问操作,如get, set, delete是通过实例的__dict__字典属性进行的,例如,对于操作a.x,会一个查找链从a.__dict['x'](实例的字典),再到type(a).__dict__['x'](类的字典),再到type(a)的父类的字典等等。
如果一个对象同时定义了__get__,__set__方法,被看作是data descriptor;只定义了__get__,被称为non-data descriptor。如果实例字典中有一个key和data descriptor同名,那么查找时优先采用data descriptor;如果实例字典中有一个key和non-data descriptor同名,那么优先采用实例字典的方法。
所以上面我们写的例子,实现a.pp可以访问,其实就是在a类调用pp函数的时候,本来是一个未绑定方法,但是这里是调用到了被ClassPro这个descriptor装饰的pp,重写了属性访问方法。之后会执行ClassPro, 而ClassPro定义了属性访问会执行该传入并运行被包装函数,入参是cls,所以就运行成功了。
同样应该说一下 property classmethod staticmethod super等都是利用了descriptor的原理实现的,这里拿classmethod的纯python实现再讲解一下
Using the non-data descriptor protocol, a pure Python version of
classmethod()
would look like this:
- 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 A(object):
- def pp(self):
- return 1
- print A.__dict__
- print A().pp()
一般正常调用pp要这样调用
但是如果我们是用上面定义的classmethod相当于成了这样的调用
- class A(object):
- def pp(self):
- return 1
- pp = classmethod(pp)
- print A.__dict__
print A.pp
中间发生了什么呢, 当我们是用classmethod方法包装pp的时候,其实就相当于使用descriptor重写了属性访问。
1. A.pp 访问相当于 A.__dict__['pp']如果没有descriptor的情况下,但是现在我们包装了descriptor classmethod就变成了 A.__dict__['pp'].__get__(None,A)
2. A.pp的使用调用了descriptor的__get__方法,传入了空object, 以及A本身。
3.
- def newfunc(*args):
- return self.f(klass, *args)
- return newfunc
返回了newfunc而newfunc 返回了 self.f也就是 pp(cls, *args) 最后返回了这个闭包。这就是运行A.pp 干的事情最后再执行() 就得到了结果。
觉得上面有点迷 可以 看下面这个应该就明白了。
- 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 A(object):
- def pp(self):
- return 1
- pp = ClassMethod(pp)
- print A.__dict__
- print A.__dict__['pp'].__get__(None, A)()
大概就是这样。
Reference:
http://stackoverflow.com/questions/17330160/how-does-the-property-decorator-work how-does-the-property-decorator-work
http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html python中基于descriptor的一些概念(上)
http://www.cnblogs.com/btchenguang/archive/2012/09/18/2690802.html#3416935 python中基于descriptor的一些概念(下)
https://docs.python.org/2.7/howto/descriptor.html Descriptor HowTo Guide
http://stackoverflow.com/questions/128573/using-property-on-classmethods Using property() on classmethods
python 中关于descriptor的一些知识问题的更多相关文章
- python中的 descriptor
学好和用好python, descriptor是必须跨越过去的一个点,现在虽然Python书籍花样百出,但是似乎都是在介绍一些Python库而已,对Python语言本身的关注很少,或者即使关注了,但是 ...
- python中基于descriptor的一些概念
python中基于descriptor的一些概念(上) 1. 前言 2. 新式类与经典类 2.1 内置的object对象 2.2 类的方法 2.2.1 静态方法 2.2.2 类方法 2.3 新式类(n ...
- python中利用matplotlib绘图可视化知识归纳
python中利用matplotlib绘图可视化知识归纳: (1)matplotlib图标正常显示中文 import matplotlib.pyplot as plt plt.rcParams['fo ...
- python中基于descriptor的一些概念(上)
@python中基于descriptor的一些概念(上) python中基于descriptor的一些概念(上) 1. 前言 2. 新式类与经典类 2.1 内置的object对象 2.2 类的方法 2 ...
- python中基于descriptor的一些概念(下)
@python中基于descriptor的一些概念(下) 3. Descriptor介绍 3.1 Descriptor代码示例 3.2 定义 3.3 Descriptor Protocol(协议) 3 ...
- python中的线程技术
#!/user/bin/env python # @Time :2018/7/7 11:42 # @Author :PGIDYSQ #@File :DaemonTest.py import threa ...
- Python中正则表达式简介
目录 一.什么是正则表达式 二.正则表达式的基础知识 1. 原子 1)普通字符作为原子 2)非打印字符作为原子 3) 通用字符作为原子 4) 原子表 2. 元字符 1)任意匹配元字符 2)边界限制元字 ...
- 盘点 Python 中的那些冷知识(二)
上一篇文章分享了 Python中的那些冷知识,地址在这里 盘点 Python 中的那些冷知识(一) 今天将接着分享!! 06. 默认参数最好不为可变对象 函数的参数分三种 可变参数 默认参数 关键字参 ...
- python模块 re模块与python中运用正则表达式的特点 模块知识详解
1.re模块和基础方法 2.在python中使用正则表达式的特点和问题 3.使用正则表达式的技巧 4.简单爬虫例子 一.re模块 模块引入; import re 相关知识: 1.查找: (1)find ...
随机推荐
- 监控nginx
vi nginx_status.sh #!/bin/bash HOST="127.0.0.1" PORT="9222" # 检测nginx进程是否存在 func ...
- js == 与 === 的区别[转]
we文章转自http://blog.sina.com.cn/s/blog_4b32835b01014iv9.html 1.对于string,number等基础类型,==和===是有区别的 1)不同类型 ...
- flask 路由和视图
路由设置的俩种方式 @app.route('/xxx') def index(): return 'index' ------------------------------------------ ...
- ubuntu 系统查看opencv 的版本
有很多的时候 ,我们想知道自己的电脑里面安装的opencv版本是多少 在终端中运行下面的命令. pkg-config --modversion opencv 为什么要知道自己电脑的opencv 版本, ...
- Android学习之六种事件响应方法汇总
java源码如下: 1.MainActivity.java源码 package com.example.responsetest; import android.app.Activity; impor ...
- Linux下ftp安装配置及三种用户的验证
一.原理简介 二.安装配置 三.三种用户的验证 一.简介 FTP即文件传输协议(File Transfer Protocol),完成各主机的文件共享功能,基于客户端-服务器的协议,工作在应用层,tcp ...
- 关于PHP程序员技术职业生涯规划
看到很多PHP程序员职业规划的文章,都是直接上来就提Linux.PHP.MySQL.Nginx.Redis.Memcache.jQuery这些,然后就直接上手搭环境.做项目,中级就是学习各种PHP框架 ...
- 讲一下Asp.net core MVC2.1 里面的 ApiControllerAttribute (转载)
ASP.NET Core MVC 2.1 特意为构建 HTTP API 提供了一些小特性,今天主角就是 ApiControllerAttribute. (注:文章是18年2月份的,所以文章提到了cor ...
- BZOJ2154/BZOJ2693/Luogu1829 Crash的数字表格/JZPFAR 莫比乌斯反演
传送门--Luogu 传送门--BZOJ2154 BZOJ2693是权限题 其中JZPFAR是多组询问,Crash的数字表格是单组询问 先推式子(默认\(N \leq M\),所有分数下取整) \(\ ...
- WPF Blend 脑洞大开的问题:如何用Blend得到或画出一个凹槽、曲面。
原文:WPF Blend 脑洞大开的问题:如何用Blend得到或画出一个凹槽.曲面. 目标图: 步骤一(放置一个矩形,填充蓝色): 步骤二(复制该矩形,并调整边角,填充粉红色): 第三部:让图形部分重 ...