python descriptor

没有看到过中文翻译,不知道对应的中文译名是什么。descriptor是指python中一个实现了__get__, __set__, __delete__方法的类.

descriptor protocol

虽然不知道为什么这种东西还是protocol,但是官网就是这么给的.三个函数的原型如下:
descr.get(self, obj, type=None) –> value
descr.set(self, obj, value) –> None
descr.delete(self, obj) –> None
对于定义的__get__和__set__的类被称为data descriptor, 只定义了__get__的被称为non-data descriptor.这两种descriptor的主要区别在于查找变量时的优先度不同.关于优先度的问题,会在后面详细描述.
如果需要read-only data descriptor的话,定义__set__方法为抛出异常就好了.

查找链

attr取值

  1. attr为python自动产生的,找到
  2. 查找obj.__class__.__dict__.若attr存在而且是data descriptor,返回其__get__的结果;没有,就在obj.__class__的父类以及祖先类中寻找data descriptor
  3. 在obj.__dict__中寻找.如果obj是一个普通实例,找到就返回.如果obj是一个类,则在obj,其父类以及祖先类的__dict__中查找,找到descriptor 返回其__get__的结果,找到普通attr则直接返回结果.
  4. 在obj.__class__.__dict__中查找,找到了descriptor(必定是non-data descriptor),调用其__get__方法,返回结果.否则下一步
  5. 抛出异常-_-|||

    attr 赋值

  6. 查找obj.__class__.__dict__.若attr存在而且是data descriptor,调用其__set__方法; 没有,就在obj.__class__的父类以及祖先类中寻找data descriptor
  7. 直接在obj.__dict__中加入obj.__dict__[‘attr’]=value

    问题

    在attr赋值的过程中,我们发现没有non-data descriptor的查找过程.也就是说如果我们对一个non-data descriptor赋值的话,会将其覆盖.

    简单地例子

    class RevealAccess(object):

    """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
        self.val = val
    

    class MyClass(object):

    x = RevealAccess(10, 'var "x"')
    y = 5
    

    m = MyClass()
    m.x
    m.x = 20
    m.y
    在python终端执行上述代码,输出为
    Retrieving var “x”
    10
    Updating var “x”
    5

    property

    一种简单的实现descriptor的方法.property是python定义的函数,原型如下:
    property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
    利用该函数可以简单的定义一个descriptor

    function and method

    区别

    function就是c中的function,在类的外面定义,可以被随时调用;method是一种特殊的function,和java中的method概念基本相同,是在类中定义的function.

    实现

    在obj.__dict__中将method以function的形式进行存储.为了支持函数调用,所用的函数都实现了__get__方法.也就是说,所有的函数都是non-data descriptor
    对于下面的例子
    class D(object):

    def f(self, x):
         return x
    

    d = D()
    d.dict[‘f’]
    D.f
    d.f
    输出分别是:


    >

static method 以及 class method

正如前文所说,method在class中是以descriptor的形式存在的.因此我们所定义的函数和其真正调用的形式是不一样的.
static method类似于java中的static方法,在调用时不会将类本身作为参数传入;而class method会将类本身作为参数传入()

Transformation | Called from an Object | Called from a Class |
———————————————| ———————————————-————— | ——————————————-——— |
function | f(obj,*args) | f(*args) |
static method | f(*args) | f(*args) |
static method | f((type(obj))*args) | f(klass,*args) |