python描述符

定义

一般说来,描述符是一种访问对象属性时候的绑定行为,如果这个对象属性定义了__get__(),__set__(), and __delete__()一种或者几种,那么就称之为描述符。描述符在属性查找的时候可以覆盖默认的属性查找行为。

如果一个对象定义了__get__()__set__()方法,那么称之为数据描述符,如果只定义了__get__()称之为非数据描述符。

描述符调用

描述符可以直接通过方法名调用,如d.__get__(obj)。也可以通过属性查找调用,如: obj.d,如果d定义了__get__(),那么就会调用d.__get__(obj)

描述符的调用实现定义在object.__getattribute__()方法中:

  • 对于实例对象b,将b.x转为type(b).__dict__['x'].__get__(b, type(b))
  • 对于类B,将B.x转为B.__dict__['x'].__get__(None, B)

__getattribute__()用纯python写的话类似下面:

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

注意点:

  1. 描述符由__getattribute__()方法调用
  2. 重载__getattribute__()方法可以屏蔽描述符的调用
  3. 描述符只能在新式类(即objects的子类)中才会起作用
  4. object.__getattribute__()type.__getattribute__()调用__get__()过程不同
  5. 属性查找时数据描述符总是覆盖实例属性(具体在python属性查找中解释)
  6. 非数据描述符可能被实例属性覆盖

实例属性指的是对于实例b,存在于b.__dict__中的属性。b.__dict__只包含有实例特有的属性,不包含类共有的属性。

描述符应用

Properties

property可以将一个类的方法构建成数据描述符,从而可以使得以类的属性调用的方式调用方法。

例如:

  1. class C(object):
  2. def getx(self): return self.__x
  3. def setx(self, value): self.__x = value
  4. def delx(self): del self.__x
  5. x = property(getx, setx, delx, "I'm the 'x' property."

从而调用c.x隐式调用c.getx()

如果Property用纯python写,那么等价于:

  1. class Property(object):
  2. "Emulate PyProperty_Type() in Objects/descrobject.c"
  3. def __init__(self, fget=None, fset=None, fdel=None, doc=None):
  4. self.fget = fget
  5. self.fset = fset
  6. self.fdel = fdel
  7. if doc is None and fget is not None:
  8. doc = fget.__doc__
  9. self.__doc__ = doc
  10. def __get__(self, obj, objtype=None):
  11. if obj is None:
  12. return self
  13. if self.fget is None:
  14. raise AttributeError("unreadable attribute")
  15. return self.fget(obj)
  16. def __set__(self, obj, value):
  17. if self.fset is None:
  18. raise AttributeError("can't set attribute")
  19. self.fset(obj, value)
  20. def __delete__(self, obj):
  21. if self.fdel is None:
  22. raise AttributeError("can't delete attribute")
  23. self.fdel(obj)
  24. def getter(self, fget):
  25. return type(self)(fget, self.fset, self.fdel, self.__doc__)
  26. def setter(self, fset):
  27. return type(self)(self.fget, fset, self.fdel, self.__doc__)
  28. def deleter(self, fdel):
  29. return type(self)(self.fget, self.fset, fdel, self.__doc__)

函数和方法

在python中,一切皆是对象。python的方法也不例外,所有python函数均是function这个类的实例,function定义了__get__()方法(使用type(方法).__dict__可以得到__get__这个属性),也就是所有函数均为非数据描述符。根据是实例调用还是类调用,描述符返回绑定方法和非绑定方法。

  1. >>> class D(object):
  2. ... def f(self, x):
  3. ... return x
  4. ...
  5. >>> d = D()
  6. >>> D.__dict__['f'] # Stored internally as a function
  7. <function f at 0x00C45070>
  8. >>> D.f # Get from a class becomes an unbound method
  9. <unbound method D.f>
  10. >>> d.f # Get from an instance becomes a bound method
  11. <bound method D.f of <__main__.D object at 0x00B18C90>>

绑定方法:实例对象调用方法时候会传出一个self参数,将这个实例对象与方法绑定。

非绑定方法:类直接调用方法属性

将function类用纯python写,等价如下:

  1. class Function(object):
  2. . . .
  3. def __get__(self, obj, objtype=None):
  4. "Simulate func_descr_get() in Objects/funcobject.c"
  5. return types.MethodType(self, obj, objtype)

type.MethodType()手动创建一个绑定方法来使用。只有当实例被使用的时候绑定方法才会被创建.

static method类和class method类

两者都是使用非数据描述符的方式构建描述符。

若使用纯python写,等价如下:

  1. class StaticMethod(object):
  2. "Emulate PyStaticMethod_Type() in Objects/funcobject.c"
  3. def __init__(self, f):
  4. self.f = f
  5. def __get__(self, obj, objtype=None):
  6. return self.f
  1. class ClassMethod(object):
  2. "Emulate PyClassMethod_Type() in Objects/funcobject.c"
  3. def __init__(self, f):
  4. self.f = f
  5. def __get__(self, obj, klass=None):
  6. if klass is None:
  7. klass = type(obj)
  8. def newfunc(*args):
  9. return self.f(klass, *args)
  10. return newfunc

python属性查找

如果有obj = Clz(), 那么obj.attr查找顺序如下:

  1. 调用__getattribute_
  2. 如果attr是出现在Clz或其基类的__dict__中,且attr是data descriptor, 那么调用其__get__方法
  3. 如果attr出现在obj的__dict__中, 那么直接返回obj.__dict__['attr']
  4. 如果attr出现在Clz或其基类的__dict__中

    4.1 如果attr是non-data descriptor,那么调用其__get__方法

    4.2 返回__dict__['attr']
  5. 如果Clz有__getattr__方法,调用__getattr__方法,否则
  6. 抛出AttributeError

属性查找中__getattribute_方法是无论如何都会被调用的,且从python描述符调用章节可知__getattribute_实际上实现了描述符的调用。如果类同时定义了__getattr__方法,只有在__getattribute_显式调用__getattr__或者__getattribute_抛出AttributeError才会调用__getattr__

注意如果要重载__getattribute_方法,需要显式调用object.__getattribute__(self, name)以防止无限循环.

例如:

  1. class test(object):
  2. t = 5
  3. def __getattribute__(self, item):
  4. if item=='t':
  5. return super(test, self).__getattribute__(item)
  6. return 6
  7. t = test()
  8. print t.t
  9. print t.s
  10. 结果为:
  11. 5
  12. 6

python属性赋值

python通过对象的__setattr__方法赋值,也可以通过setattr方法,事实上setattr就是通过调用__setattr__完成赋值。属性赋值也会受到数据描述符影响。

举例来说:x.b=3,属性赋值过程如下:

  1. 如果类重载了__setattr__方法,优先调用__setattr__
  2. 如果没有重载__setattr__方法,且b是数据描述符,就会调用b.__set__()
  3. 如果以上都没有,调用obj.__dict__['b'] = 3__setattr__方法的默认行为)

推测跟object.__getattribute__()一样,object.__setattr__()中实现了数据描述符的调用。

参考资料

https://docs.python.org/2/howto/descriptor.html

https://www.cnblogs.com/xybaby/p/6270551.html

https://zhuanlan.zhihu.com/p/48185978

https://zhuanlan.zhihu.com/p/48185978

python描述符和属性查找的更多相关文章

  1. python之属性描述符与属性查找规则

    描述符 import numbers class IntgerField: def __get__(self, isinstance, owner): print('获取age') return se ...

  2. Python 属性描述符和属性的查找过程

    属性描述符可以用来控制给属性赋值的时候的一些行为 import numbers class IntField: def __get__(self, instance, owner): return s ...

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

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

  4. python高级编程之最佳实践,描述符与属性01

    # -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' #最佳实践 """ 为了避免前面所有的 ...

  5. python描述符 descriptor

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

  6. Python描述符的使用

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

  7. Python描述符 (descriptor) 详解

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

  8. Python 描述符 (descriptor)

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

  9. 杂项之python描述符协议

    杂项之python描述符协议 本节内容 由来 描述符协议概念 类的静态方法及类方法实现原理 类作为装饰器使用 1. 由来 闲来无事去看了看django中的内置分页方法,发现里面用到了类作为装饰器来使用 ...

随机推荐

  1. sql-查看执行计划的方法

    sql执行计划:把SQL语句拆分为每个的操作步骤组合,按照一定的顺序执行得出结果,查看并看懂执行计划是调优的关键步骤 查看执行计划的方法 DBMS_XPLAN包 sql*plus AUTO trace ...

  2. Android 度量单位

    单位 注释 px(像素) 每个像素对应手机上的一个点,在不同设备上1px表示的长度不一定相同 screen size(屏幕尺寸) 指手机对角线的长度,如4.7英寸 resolution(分辨率) 指屏 ...

  3. 电信流氓注入JS

    (function () { var cs_url = _pushshowjs_.url, cs_delay = window.cs_delay; var cs_styles = window.sty ...

  4. Glidar测试安装

    在上一篇随笔中,我们完成了对Glidar 仿真器的概念层面的认识.接下来,我们将着手对该该仿真器进行安装测试. 1 依赖库的安装 安装环境为Windows 7 64位+Ubuntu14.04 LTS的 ...

  5. PIC18F26K20

    Clock Four Crystal modes, Two External clock modes,  Two RC Oscillator, Internal oscillator, PLL

  6. C语言-实现字符串倒序输出

    方法1: Action(){//倒序输出 char *src="abcdefgh123"; char *desc; desc=(char *)malloc(100*sizeof(c ...

  7. 强化学习(2)----Q-learning

    1.Q-learning主要是Q表: 当前状态s1,接下来可以有两个动作选择,看电视a1和学习a2,对于agent人来说,可以根据reward来作出决策(Policy).目的就是得到奖励最大. Q-l ...

  8. str 数据类型的用法

    ---------------------------------------------------------------------------------------------------- ...

  9. pip 出错

    pip 升级到10以上出错 ImportError: cannot import name 'main' 解决方法一: 降低pip的版本号 python -m pip install pip==9.0 ...

  10. 紫书 例题11-8 UVa 11082(网络流最大流)

    这道题的建模真的非常的秀, 非常牛逼. 先讲建模过程.源点到每一行连一条弧, 容量为这一行的和减去列数, 然后每一列到汇点连一条弧, 容量为这一列 的和减去行数, 然后每一行和列之间连一条弧, 容量为 ...