本文转载自https://zhuanlan.zhihu.com/p/366156798,并结合自己的理解整理下面这篇文章,供学习记录使用,如有冒犯,请联系删除。
python @property
装饰器使一个方法可以像属性一样被使用,而不需要在调用的时候带上()
。接下来我们会深入了解一下我们什么时候需要使用它,并且在什么场景下需要用到它以及如何很好的使用它。
你在看review别人代码的时候,可能看到过在方法上添加property 装饰器的场景。不过在深入了解之前,你需要对python中的class 有一定的了解,因为通常我们使用property 装饰器就用在类中。
首先我们创建一个person
类,类中包含first
,last
和 fullname
属性,并且包含一个email()
方法,可以提供一个人的email。
class Person():
def __init__(self, firstname, lastname):
self.first = firstname
self.last = lastname
self.fullname = self.first + ' '+ self.last
def email(self):
return '{}.{}@email.com'.format(self.first, self.last)
接下来我们创建一个Person 实例,并打印属性
# 创建一个person对象
person = Person('zhang', 'san')
print(person.first)
print(person.last)
print(person.fullname)
print(person.email())
# 输出为
zhang
san
zhang san
zhang.san@email.com
到这里还没有发生什么问题,但是突然zhang san 改名为zhang si了,你也需要更改zhang san 这个对象的属性,但是这个时候你就会发现,你在更改zhang san的名字之后,它其他的属性并没有自动的改变。
比如,当你修改self.last
的时候,你会希望self.fullname
会随着self.last
的改变而自动更新。但是self.fullname
并没有更新,这个属性的值就可能误导使用者 。
但是email()
方法输出的内容就可以随着self.last
的更改而更新
# 改变last name的时候,并没有改变fullname
person.last = 'si'
print(person.first)
print(person.last)
print(person.fullname)
print(person.email())
zhang
si
zhang san
zhang.si@email.com
这时候,作为大聪明的你就可能会想到,我们把self.fullname
属性改为fullname()
方法,我们不就可以正常使用了嘛,比如:
# 将fullname属性,改为fullname()方法
# 但是对于没有使用fullname()方法的旧代码,可以就没办法使用person.fullname的方式调用了
# 所以这样更改后,要修改所有的旧代码
class Person():
def __init__(self, first_name, last_name):
self.first = first_name
self.last = last_name
def fullname(self):
return self.first + ' ' + self.last
def email(self):
return '{}.{}@email.com'.format(self.first, self.last)
person = Person('zhang', 'san')
print(person.fullname())
person.last = 'si'
print(person.fullname())
我们发现使用这种方式来促使fullname自动进行更新是没有问题的。
但是我们通过使用person.fullname()
这种带有() 括号的方式来调用,而不是像调用属性那样,直接使用person.fullname
来获得属性,这就会使之前使用person.fullname
来获得属性的方式全部失效。如果你这个属性是放在一个工具类中,你的同事使用你写的工具类进行开发,当你将这个属性改为方法时,之前所有导入你写的工具类的代码都将不能正常工作。
因此,如果可以将这个方法转化为一个属性,那就可以兼顾两者,不改代码量的基础上实现两种功能。之前我们说过@property
可以使类中的方法,可以像类中属性一样的方式被调用。于是,可以加上一个**@property** 装饰器来解决这个问题。在方法定义上面加一个**@property** 装饰器,在不改变原有调用方式的同时,来将一个属性改为一个方法。
# 添加@property属性,可以在不改变原有调用规则的基础上,获得正确的fullname
class Person():
def __init__(self, first_name, last_name):
self.first = first_name
self.last = last_name
@property
def fullname(self):
return self.first + ' ' + self.last
def email(self):
return '{}.{}@email.com'.format(self.first, self.last)
# 初始化一个Person对象
person = Person('zhang', 'san')
print(person.fullname)
# 修改last_name
person.last = 'si'
print(person.fullname)
#输出结果
zhang san
zhang si
现在你可以像调用属性一样,来使用person.fullname
方法。
但是另一个问题出现了,有时候我想直接更改fullname
,并且更改fullname
的同时,我希望我的first
和 last
可以跟着一起变。
但是当我们尝试设置fullname
时,却报错了
class Person():
def __init__(self, first_name, last_name):
self.first = first_name
self.last = last_name
@property
def fullname(self):
return self.first + ' ' + self.last
def email(self):
return '{}.{}@email.com'.format(self.first, self.last)
person = Person('zhang', 'san')
person.fullname = 'zhang wu'
我们可以定义一个setter
方法,每当为这个属性设置值时都用被调用
在setter
方法中,当fullname
被修改后,对应的变量也会随着改变
当你使用setter
方法时,你需要遵循以下规则:
setter
方法需要和@property
修饰的方法具有相同的名字property
的值,作为参数@{methodname}.setter
装饰器当你添加@{methodname}.setter
去装饰一个方法时,这个方法就会在(本例中为fullname
)属性被赋值时所调用。比如:
class Person():
def __init__(self, first_name, last_name):
self.first = first_name
self.last = last_name
@property
def fullname(self):
return self.first + ' ' + self.last
@fullname.setter
def fullname(self, name):
first_name, last_name = name.split()
self.first = first_name
self.last = last_name
def email(self):
return '{}.{}@email.com'.format(self.first, self.last)
person = Person('zhang', 'san')
print(person.fullname)
print(person.last)
print(person.first)
person.fullname = 'li si'
print(person.fullname)
print(person.last)
print(person.first)
#运行结果
zhang san
san
zhang
li si
si
li
我们发现,当我们给fullname
一个新值的时候,last
和first
也会随之改变。现在我们的Person
类就可以自动的更新属性,当其中属性变化时。
和setter
方法类似,当我们需要删除一个属性时,我们会使用deleter
方法。
你可以像定义setter
方法一样来定义一个setter
方法,使用相同的方法名,并在方法上添加@{methodname}.deleter
装饰器 。
class Person():
def __init__(self, first_name, last_name):
self.first = first_name
self.last = last_name
@property
def fullname(self):
return self.first + ' ' + self.last
@fullname.setter
def fullname(self, name):
first_name, last_name = name.split()
self.first = first_name
self.last = last_name
@fullname.deleter
def fullname(self):
self.first = None
self.last = None
def email(self):
return '{}.{}@email.com'.format(self.first, self.last)
person = Person('zhang', 'san')
print(person.fullname)
print(person.last)
print(person.first)
del person.fullname
print(person.last)
print(person.first)
#运行结果
zhang san
san
zhang
None
None
看上面的输出结果,我们不难发现,当删除fullname
后,first
和 last
都被设置为空了
@property
装饰器呢?当一个B属性的值,是通过另一个A属性得来的。并且这个B属性会随着初始A属性得值得改变而更新
@property
我们通过在方法定义上面添加@property 来使一个方法可以像调用属性那样调用
property
特性得方法,添加setter
?当你设置B属性的值时,并且你想同时更新A属性,这个就可以通过添加setter 来实现。