Python 中的 property

使用 property 一般有以下两种方式

通过装饰器

class Demo(object):

    def __init__(self, val):
        self._x = val

    # 经过装饰器后,x 为 property对象
    @property
    def x(self):
        # after some opertation
        return self._x

    # 调用 x 的setter方法(一个装饰器) 返回新的 property 对象
    @x.setter
    def x(self, val):
        self._x = val

    @x.deleter
    def x(self):
        print 'del x'

通过创建 property 的实例

class Demo2(object):

    def __init__(self, val):
        self._x = val

    def getx(self):
        return self._x

    def setx(self, val):
        self._x = val

    def delx(self):
        print 'del x'

    x = property(getx, setx, delx)

下面是一个使用 __get__ __set__ 的等价 property 实现 参考自 http://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html

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
        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 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__)

假如创建了 Demo 类的实例 demo

当调用 demo.x 时, 会调用 Property 的 __get__ 方法

此时的参数 obj 为 demo。通过调用注册好的 fget() 来返回 _x

一个奇怪的例子,

大概没人会这么写
class Demo3(object):

    def __init__(self, val):
        self._x = val
        self.x = property(self.getx, self.setx, self.delx)

    def getx(self):
        return self._x

    def setx(self, val):
        self._x = val

    def delx(self):
        print 'del x'

demo = Demo3(0)
demo.x
# <property at 0x7f26e24ae890>

这是因为

demo.x 首先会调用 __getattribute__ 方法,在 Demo3 的字典中找 x ,如果 x 定义了 __get__ 方法,那么 x. get (demo, Demo3)

此过程等价于

type(demo).__dict__['x'].__get__(demo, type(demo))

那么我们尝试

demo.x.__get__(demo, type(demo))

TypeError: getx() takes exactly 1 argument (2 given)

这是因为我们通过实例去调用的 property ,那么我们调用 getx 时自然会和 self 绑定,所以这里多传了一个参数

所以这么改改

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
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError, "unreadable attribute"
        # 不需要传 obj
        return self.fget()
我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章