@property的作用:
我们在定义类的时候,往往会对类的一些属性进行读取与修改,我们可以很方便的用类似于:对象.属性 这样的方式来达到我们的目的,但这种方式很不安全,因为用户在对属性进行修改操作时,用户的输入值没有经过任何校验,数据类型和值域范围完全不可控,用户的随意输入可能会造成灾难性后果。如下面所示:
class Student:
def __init__(self, name, score):
self.name = name
self._score = score
s = Student('Bob', 59)
s._score = 60
s._score = 1000 #这个分数肯定是有误的
我们可以这样做:
class Student:
def __init__(self, name, score):
self.name = name
self._score = score
def get_score(self):
return self._score
def set_score(self, score):
if not isinstance(value,int)
raise ValueError('score must be integer')
if score < 0 or score > 100:
raise ValueError('invalid score')
self._score = score
这样一来,s.set_score(1000) 就会报错。看到上图代码中新定义的方法,学过Java的读者会觉得很眼熟,这不就是Java Bean的规范吗?其实除了看起来相似之外,两者并不相同。在Java中通常将类属性变量设置为私有,并在类中定义getter和setter方法,在外部只有通过getter和setter方法才能访问属性值,这样做的好处是可以保证属性变量的数值安全。而python中并没有私有变量这一概念,上图中的改写虽然实现了输入校验的功能,但写起来略显繁琐,对用户也不够友好。Python提供了更加优秀的解决方案“@property”。因为Python支持高阶函数,可以用装饰器函数把 get/set 方法“装饰”成属性调用:
class Student:
def __init__(self, name, score):
self.name = name
self._score = score
@property
def score(self):
return self._score
@score.setter
def score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self._score = score
像使用属性一样设置score了:
两个方法名完全一致,但在用了@property和@score.setter装饰后,不仅可以并行不悖,还能大大简化在类外部对属性的访问,这正是python语法糖的神奇之处。
>>> s = Student('Bob', 59)
>>> s.score = 60
>>> print s.score
60
>>> s.score = 1000
Traceback (most recent call last):
...
ValueError: invalid score
提醒:
类中的普通属性了,会与原本的score属性冲突,因此建议读者平时写代码时养成良好的习惯,在定义类属性时使用下划线作为前缀。