本质上说,本文提到的 @property 和上一篇文章提到的 @classmethod、@staticmethod 都是装饰器,都是对函数定义进行补充使之具有特殊功能。
正文
一、
设想:当我定义了一个用户类:
class User(Model):
...
password = db.Column(db.String(64))
...
在上例中,很明显,password
是可以显式调用的属性,这在网站设计中绝不可以出现,会造成用户数据泄露的极大风险。那么,要将其变为私有属性,需要修改如下:
class User(Model):
...
__password = db.Column(db.String(64))
这样,当创建实例变量后,再访问此属性会抛出 AttributeError
异常:
AttributeError: 'User' object has no attribute '__password'
这是一种解决办法,可是,当我们需要获取或者修改这个属性的值时,该怎么办呢?
只能通过为类增加实例方法来实现,方法中还可以加入参数检查的功能:
class User(Model):
def __init__(self, name, score):
self.__name = name
self.__score = score
def get_score(self):
return self.__score
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
rasie ValueError('bad score')
但上面的方法略显复杂,python 提供了 @property 装饰器,可以轻松实现上述功能。
二、
创建一个只读属性:
class User(object):
...
@property
def score():
return __score
...
使用 @property 可以将一个方法变为属性,无 @property 时,需要 u.score()
;使用之后,直接调用 u.score
即可。
对于网站设计来说,常采用的做法是:将密码设置为只写属性,存储时,只存储其与其他用户属性生成的散列值(如用户密码+用户邮箱):
class User:
@property
def password(self):
raise AttributeError('Password is not accessable.')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
当我们设置密码时,就可以使用 u.password = 'xxxx'
了。
在这个过程中,@property 又产生了另一个装饰器 @password.setter 将 password 方法变成属性赋值。
在 Python 的官方文档中,除了 getter
、setter
外,还有一个 deleter
:
class User:
@property
def score():
return __score
@score.deleter
def score(self):
del score
顾名思义,这个派生出来的装饰器可以用来删除用户属性。
三、
在官方文档中,@property 装饰器还被这样使用:
class C:
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
这样,u 是 C 的实例,当访问 u.x
时,将调用 u.getx()
方法。
u.x = value
将调用 u.setx(value)
方法。
del u.x
将调用 u.delx()
方法。
四、
@property 能将方法变成属性,又可在方法中加入参数检查的功能,这将它可普通属性区别开来,同时比定义额外的 get/set 方法清晰明了。将方法变成属性,这是 @property 的精髓。
参考链接
- 使用@property,by 廖雪峰