@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属性冲突,因此建议读者平时写代码时养成良好的习惯,在定义类属性时使用下划线作为前缀