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, "cant set attribute"
self.fset(obj, value)

def __delete__(self, obj):
if self.fdel is None:
raise AttributeError, "cant 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
# 

这是因为

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()
  • 版权声明: 本文源自互联网, 于3个月前,由整理发表,共 2095字。
  • 原文链接:点此查看原文