Loading... ## 声明 本文转载自**https://zhuanlan.zhihu.com/p/366156798**,并结合自己的理解整理下面这篇文章,供学习记录使用,如有冒犯,请联系删除。 ## 概述 python `@property` 装饰器使一个方法可以像属性一样被使用,而不需要在调用的时候带上`()` 。接下来我们会深入了解一下我们什么时候需要使用它,并且在什么场景下需要用到它以及如何很好的使用它。 ## 一、@property简介 你在看review别人代码的时候,可能看到过在方法上添加property 装饰器的场景。不过在深入了解之前,你需要对python中的class 有一定的了解,因为通常我们使用property 装饰器就用在类中。 ## 二、 没有@property会发生的尴尬情况 首先我们创建一个`person` 类,类中包含`first`,`last` 和 `fullname` 属性,并且包含一个`email()` 方法,可以提供一个人的email。 ```python 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 实例,并打印属性 ```python # 创建一个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` 的更改而更新 ```python # 改变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()` 方法,我们不就可以正常使用了嘛,比如: ```python # 将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** 装饰器,在不改变原有调用方式的同时,来将一个属性改为一个方法。 ```python # 添加@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 ``` ## 四、setter方法-什么时候使用它并且如何写一个setter方法 现在你可以像调用属性一样,来使用`person.fullname` 方法。 但是另一个问题出现了,有时候我想直接更改`fullname`,并且更改`fullname` 的同时,我希望我的`first` 和 `last` 可以跟着一起变。 但是当我们尝试设置`fullname`时,却报错了 ```python 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`)属性被赋值时所调用。比如: ```python 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` 类就可以自动的更新属性,当其中属性变化时。 ## 五、deleter方法 和`setter` 方法类似,当我们需要删除一个属性时,我们会使用`deleter` 方法。 你可以像定义`setter` 方法一样来定义一个`setter` 方法,使用相同的方法名,并在方法上添加`@{methodname}.deleter` 装饰器 。 ```python 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` 都被设置为空了 ## 六、总结 1. 什么时候使用`@property` 装饰器呢? 当一个B属性的值,是通过另一个A属性得来的。并且这个B属性会随着初始A属性得值得改变而更新 2. 如何创建一个`@property` 我们通过在方法定义上面添加@property 来使一个方法可以像调用属性那样调用 3. 我们什么时候对具有`property`特性得方法,添加`setter` ? 当你设置B属性的值时,并且你想同时更新A属性,这个就可以通过添加setter 来实现。 最后修改:2025 年 02 月 23 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏