这个问题从早上日常扫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的一些知识问题的更多相关文章

  1. python中的 descriptor

    学好和用好python, descriptor是必须跨越过去的一个点,现在虽然Python书籍花样百出,但是似乎都是在介绍一些Python库而已,对Python语言本身的关注很少,或者即使关注了,但是 ...

  2. python中基于descriptor的一些概念

    python中基于descriptor的一些概念(上) 1. 前言 2. 新式类与经典类 2.1 内置的object对象 2.2 类的方法 2.2.1 静态方法 2.2.2 类方法 2.3 新式类(n ...

  3. python中利用matplotlib绘图可视化知识归纳

    python中利用matplotlib绘图可视化知识归纳: (1)matplotlib图标正常显示中文 import matplotlib.pyplot as plt plt.rcParams['fo ...

  4. python中基于descriptor的一些概念(上)

    @python中基于descriptor的一些概念(上) python中基于descriptor的一些概念(上) 1. 前言 2. 新式类与经典类 2.1 内置的object对象 2.2 类的方法 2 ...

  5. python中基于descriptor的一些概念(下)

    @python中基于descriptor的一些概念(下) 3. Descriptor介绍 3.1 Descriptor代码示例 3.2 定义 3.3 Descriptor Protocol(协议) 3 ...

  6. python中的线程技术

    #!/user/bin/env python # @Time :2018/7/7 11:42 # @Author :PGIDYSQ #@File :DaemonTest.py import threa ...

  7. Python中正则表达式简介

    目录 一.什么是正则表达式 二.正则表达式的基础知识 1. 原子 1)普通字符作为原子 2)非打印字符作为原子 3) 通用字符作为原子 4) 原子表 2. 元字符 1)任意匹配元字符 2)边界限制元字 ...

  8. 盘点 Python 中的那些冷知识(二)

    上一篇文章分享了 Python中的那些冷知识,地址在这里 盘点 Python 中的那些冷知识(一) 今天将接着分享!! 06. 默认参数最好不为可变对象 函数的参数分三种 可变参数 默认参数 关键字参 ...

  9. python模块 re模块与python中运用正则表达式的特点 模块知识详解

    1.re模块和基础方法 2.在python中使用正则表达式的特点和问题 3.使用正则表达式的技巧 4.简单爬虫例子 一.re模块 模块引入; import re 相关知识: 1.查找: (1)find ...

随机推荐

  1. warning: ISO C++ forbids converting a string constant to 'char*'

    第1种字符串赋值方式: char * fileName="./2017-09-02-10-34-10.xml";//这一种字符串赋值方式已经被ISO禁止了 第2种字符串赋值方式: ...

  2. solvepnp

    CV_EXPORTS_W bool solvePnP( InputArray objectPoints,- 世界坐标系下的控制点的坐标,vector<Point3f>的数据类型在这里可以使 ...

  3. 洛谷 P1525 关押罪犯

    题目链接 https://www.luogu.org/problemnew/show/P1525 题目描述 S城现有两座监狱,一共关押着N名罪犯,编号分别为1−N.他们之间的关系自然也极不和谐.很多罪 ...

  4. 3.4《想成为黑客,不知道这些命令行可不行》(Learn Enough Command Line to Be Dangerous)——grepping(检索目标行命令)

    grep是检查文件内容最强大的工具之一,这也许不能代表什么,但这不是重点.的确,grep常用作动词,比如'你完全应该检索(grep)那个文件'. grep最常用于在文件中搜索子字符串.例如,我们在第三 ...

  5. Luogu4338 ZJOI2018 历史 LCT、贪心

    传送门 题意:在$N$个点的$LCT$中,最开始每条边的虚实不定,给出每一个点的$access$次数,求一种$access$方案使得每条边的虚实变换次数之和最大,需要支持动态增加某个点的$access ...

  6. LiveCharts文档-3开始-5序列Series

    原文:LiveCharts文档-3开始-5序列Series LiveCharts文档-3开始-5序列Series Strokes和Fills 笔触和填充 所有的Series都有笔触和填充属来处理颜色, ...

  7. WPF中DataGrid中的DataGridCheckBoxColumn用法(全选,全否,反选)

    原文:WPF中DataGrid中的DataGridCheckBoxColumn用法(全选,全否,反选) 前台代码 <DataGrid.Columns> <DataGridCheckB ...

  8. Ubuntu 16.04 下部署Node.js+MySQL微信小程序商城

    转载于这篇文章 关于pm2看这篇文章 最近在研究小程序,申请了域名之后,再一次来配置环境,根据作者的步骤基本上完成了网站的架构,但由于环境路径等不同,配置上会有所不同,因此记录下来. 1.更新系统和安 ...

  9. openMP多线程编程

    OpenMP(Open Muti-Processing) OpenMP缺点: 1:作为高层抽象,OpenMp并不适合需要复杂的线程间同步和互斥的场合: 2:另一个缺点是不能在非共享内存系统(如计算机集 ...

  10. Ubuntu16.04密码正确 进不去桌面系统(已测试恢复正常)

    遇到过两次ubuntu输入密码正确,但是进不去系统,输入密码后,跳转到一下界面 之后又返回到登陆界面,一直这样循环输入密码. Guest用户可以.   解决办法: 1.进入tty下           ...