Python 描述器解析

语法简析  

1、一般来说,描述器(descriptor)是一个有”绑定行为”的对象属性(object attribute),它的属性访问被描述器协议方法重写。这些方法是 __get__()__set__()__delete__()
如果一个对象定义了以上任意一个方法,它就是一个描述器。而描述器协议的具体形式如下:
descr.__get__(self, obj, type=None) --> value

descr.__set__(self, obj, value) --> None

descr.__delete__(self, obj) --> None

2、描述器本质上是一个类对象,该对象定义了描述器协议三种方法中至少一种。

3、而这三种方法只有当类的实例出现在一个所有者类(owner class)之内时才有效,也就是说,描述器必须出现在所有者类或其父类的字典  __dict__  里。

4、以上提到了两个类,一是定义了描述器协议的描述器类,另一个是使用描述器的所有者类。

5、描述器往往以装饰器的方式被使用,导致二者常被混淆。

6、描述器类和不带参数的装饰器类一样,都传入函数对象作为参数,并返回一个类实例;所不同的是,装饰器类返回 callable 的实例,描述器则返回描述器实例。

举例说明

一、@Property

Python 内置的property  函数可以说是最著名的描述器之一,几乎所有讲述描述器的文章都会拿它做例子。

 property 是用 C 实现的,不过这里有一份等价的 Python 实现:

class Property(object):
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can‘t set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can‘t delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

 Property  怎么用呢?看下面的例子:

class C(object):
    def __init__(self):
        self._x = None

    @Property
    def x(self):
        """I‘m the ‘x‘ property."""
        return self._x

    @x.setter
    def x(self, value):
        assert value > 0
        self._x = value

    @x.deleter
    def x(self):
        del self._x

结合源代码和用法来分析  Property :

@Property的用法就是一个装饰器。我们可以将其等价转化为:

x = Property(x)

函数 x 作为位置参数被赋给  Property.__init__()  的 fget,得到新的 x 已经不是个函数而是个完整实现了 __get__()  方法的描述器实例了。

二、@x.setter

 @x.setter  的用法略有不同。它实际上是利用上面定义的描述器实例 xsetter 方法,重新创建了新的实例。这时变量 x 再次被更新,指向了一个完整实现 __get__()__set__() 方法的新描述器。传入 setter 方法的函数名必须是 x,否则如果是 y,按照装饰器的性质,
y = x.setter(y)

新描述器就被 y 引用了,与需求不符。




Python 描述器解析

上一篇:Java NIO 中 Buffer 和 Channel 的简单介绍


下一篇:云效NPM私有仓库?不限容量免费用