【python】 @property属性详解

声明

本文转载自https://zhuanlan.zhihu.com/p/366156798,并结合自己的理解整理下面这篇文章,供学习记录使用,如有冒犯,请联系删除。

概述

python @property 装饰器使一个方法可以像属性一样被使用,而不需要在调用的时候带上() 。接下来我们会深入了解一下我们什么时候需要使用它,并且在什么场景下需要用到它以及如何很好的使用它。

一、@property简介

你在看review别人代码的时候,可能看到过在方法上添加property 装饰器的场景。不过在深入了解之前,你需要对python中的class 有一定的了解,因为通常我们使用property 装饰器就用在类中。

二、 没有@property会发生的尴尬情况

首先我们创建一个person 类,类中包含first,lastfullname 属性,并且包含一个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** 装饰器,在不改变原有调用方式的同时,来将一个属性改为一个方法。

# 添加@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 的同时,我希望我的firstlast 可以跟着一起变。

但是当我们尝试设置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 一个新值的时候,lastfirst 也会随之改变。现在我们的Person 类就可以自动的更新属性,当其中属性变化时。

五、deleter方法

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 后,firstlast 都被设置为空了

六、总结

  1. 什么时候使用@property 装饰器呢?

当一个B属性的值,是通过另一个A属性得来的。并且这个B属性会随着初始A属性得值得改变而更新

  1. 如何创建一个@property

我们通过在方法定义上面添加@property 来使一个方法可以像调用属性那样调用

  1. 我们什么时候对具有property特性得方法,添加setter

当你设置B属性的值时,并且你想同时更新A属性,这个就可以通过添加setter 来实现。

  • 27
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值