一、为什么要将方法封装成属性
假设我们定义了一个student类,他有score属性。这样直接把属性暴露出来,不管是修改成绩还是读取成绩都非常方便,但是带来的问题是无法检查参数是否合法(python没有办法强制要求变量类型),导致成绩可以随意修改:
class student():
def __init__(self,name,score = 0):
self.name = name
self.score = score
a = student('wang',20)
print(a.score) #[OUT]:20
a.score = 999
print(a.score) #[OUT]:999
a.score = '^_^'
print(a.score) #[OUT]:'^_^'
初始定义a的成绩为20,但是可以随意修改为999,甚至恶作剧般的将成绩改成字符串,这当然不符合我们设计的初衷。
为了限制score的范围和类型,我们可以通过set_score()方法设置成绩,在通过get_score()方法获取成绩。这样,在set_score()方法里面就可以检查参数:
class student():
def __init__(self,name):
self.name = name
def get_score(self):
return self._score
def set_score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
a = student('wang')
a.set_score(60)
print(a.get_score()) #[OUT]:999
a.set_score(999) #[OUT]: ValueError: 'score must between 0 ~ 100!'
print(a.get_score())
现在就不能随便设置score了,但是上面的操作比较繁琐,没有直接使用属性那么简单。
有没有既能检查参数,又能使用类似属性这样简单的方法来访问类的变量呢。。。。嘿嘿,这就谈到了我们的主题,将方法封装成一个属性。意思就是说,一个东西本质上是个类的方法,但是我们可以把它当做属性来使用。这就是@property的作用了
二、@property如何使用
@property是个装饰器,和普通的装饰器用法相同,直接放在函数定义的上一行就可以了。如下面的例子,在score()函数的定义上面放上@property装饰器,我们就可以像属性一样的使用score了(而不是函数调用)。@property把一个getter方法变成属性,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作。其实同时还会生成一个不常用的装饰器@score.deleter,在进行删除该属性的时候调用。
class student():
def __init__(self,name):
self.name = name
@property
def score(self):
return self._score
@score.setter
def score(self,value):
self._score = value
@score.deleter
def score(self):
raise AttributeError("Can't delete attribute")
a = student('wang')
a.score = 10
print(a.score)
@property有三个相关联的方法,这三个方法的名字都必须一样。 第一个方法是一个 getter
函数,它使得 score
成为一个属性。 其他两个方法给score
属性添加了 setter
和 deleter
函数。 需要强调的是只有在 score
属性被创建后, 后面的两个装饰器 @score.setter
和 @score.deleter
才能被定义。
property的一个关键特征是它看上去跟普通的attribute没什么两样, 但是访问它的时候会自动触发 getter
、setter
和 deleter
方法。当然getter
、setter
和 deleter
这三个方法不必都实现,通常对于一个只读的属性,只需要定义getter方法就可以,如果允许读和修改,则需要定义getter和setter方法;deleter类似。
三、总结
@property
广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。对参数的检查放在setter方法中。还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性。
参考资料: