Python中的内置装饰器之property

装饰器是Python中很重要的一个概念,但是这篇文档不介绍装饰器的实现,我们只需要知道装饰器可以实现一些功能;

Python中也包含一些自带的装饰器,这次就说一下属性装饰器property

 

相关知识点


 Python中的实例属性和私有属性

 

用法


property装饰器的作用是让一个类方法的访问方式变成属性的访问方式,用法如下:

1 class Num:
2 
3     @property
4     def x(self):
5         return '用类方法定义,用property装饰的x'
6 
7 a = Num()  # 把C实例化,命名为a
8 print(a.x)  # 我们访问了a实例的x方法,但是没有对x做调用,和访问实例属性的方法一样

输出:用类方法定义,用property装饰的x

 

作用


使用这种方式来定义实例属性,我们就可以在类属性x中对x做一定的校验,这一点我们在后面再说。

 

衍生装饰器

举一个数字相加的例子:

class Num:

    @property
    def x(self):
        return 1

    @property
    def y(self):
        return 2

    def printf(self):
        print(f'{self.x}加{self.y}的和是:{self.x+self.y}')

a = Num()  # 把C实例化,命名为a
a.printf()

这个例子和上面的例子一样,我们定义了属性x和y,输出了x+y的和

这个例子的输出是:1加2的和是:3

 

这个时候我们希望可以在外部修改x的值,就会发现无法修改,因为本质上x是一个方法,而不是类属性:

 1 class Num:
 2 
 3     @property
 4     def x(self):
 5         return 1
 6 
 7     @property
 8     def y(self):
 9         return 2
10 
11     def printf(self):
12         print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
13 
14 a = Num()  # 把C实例化,命名为a
15 a.x = 2
16 print(a.x)

输出:AttributeError: can't set attribute

 

所以我们需要另外在类中定义一个方法,来修改x的值,相当于给外部创建一个API接口,让外部通过这个API来修改,这就用到了setter装饰器:

setter装饰器

用法

 

为了可以修改x,我们把x的值1写成实例属性self.num,

当然这个例子中外部也可以直接通过修改num的值来修改x的值,不过这个不要紧,因为只是为了方便理解,

实际使用中我们可以把self.num 写成私有属性,来保障不会被外部直接修改值,

 

写一个x属性同名方法,并且用x属性同名的setter装饰器(x.setter)来装饰同名方法x,然后在这个方法下实现对self.num的值的修改:

 1 class Num:
 2 
 3     def __init__(self):
 4         self.num = 1
 5 
 6     @property
 7     def x(self):
 8         return self.num
 9 
10     @x.setter
11     def x(self, item):
12         self.num = item
13 
14     @property
15     def y(self):
16         return 2
17 
18     def printf(self):
19         print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
20 
21 a = Num()  # 把C实例化,命名为a
22 a.x = 2
23 a.printf()

输出:2加2的和是:4

这样我们就可以通过a.x = 2来给self.num重新赋值,这样做的好处是不改变外层修改x值的方式,来实现对property属性的修改,这也是让代码更高可用,更可读很重要的一点。

 

deleter装饰器

用法


同setter装饰器,不再详细说明,不同的地方在于被deleter装饰的同名x方法不接收参数,因为我们只是删除了self.num,不需要接收参数:

 1 class Num:
 2 
 3     def __init__(self):
 4         self.num = 1
 5 
 6     @property
 7     def x(self):
 8         return self.num
 9 
10     @x.setter
11     def x(self, item):
12         self.num = item
13 
14     @x.deleter
15     def x(self):
16         del self.num
17 
18     @property
19     def y(self):
20         return 2
21 
22     def printf(self):
23         print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
24 
25 a = Num()  # 把C实例化,命名为a
26 a.x = 2
27 a.printf()
28 del a.x
29 print(a.x)

输出:2加2的和是:4

AttributeError: 'Num' object has no attribute 'num'

 

deleter装饰器装饰的方法就可以被使用del 来删除对应的属性,删除属性的操作我们就再同名方法x中进行了实现,

这样外部删除属性的方法也和删除一个属性的方法保持一致,同样保证了高可用性和可读性。

 

作用


 

现在可以说明文章一开始说的作用了,

我们通过这种property,setter,deleter装饰器的方式,把实例属性self.num写成property属性,我们就可以完成一个操作,

对外部修改、删除属性的操作,做进一步的过滤和限制

还是同样的例子,我们希望可以限制上层代码实例化并且修改x的值为非int类型,因为这会造成我们的求和方法报错,我们就可以通过这种方式实现:

 1 class Num:
 2 
 3     def __init__(self):
 4         self.num = 1
 5 
 6     @property
 7     def x(self):
 8         return self.num
 9 
10     @x.setter
11     def x(self, item):
12         if isinstance(item, int):
13             self.num = item
14         else:
15             print('不要把非int类型赋值给x!!!')
16 
17     @property
18     def y(self):
19         return 2
20 
21     def printf(self):
22         print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
23 
24 a = Num()  # 把C实例化,命名为a
25 a.x = '1234567'  # 突发奇想把x的值改为str类型

输出:不要把非int类型赋值给x!!!

 


综上,这样就可以保障上层在修改x的值的时候,可以被我们的同名装饰器x先做进一步校验,

当然你想要先对入参item做一些其他操作也一样可以,操作之后,再更改self.num为item,

这个时候printf 求和方法,就可以安全使用 + 运算符求和了。

 

总结


使用property装饰器装饰的方法可以被上层以调用属性的方式来调用,类本身也可以,可以理解为property属性,也是一个属性;

使用property属性同名.setter装饰器装饰的同名方法,提供property=xx 的方法;

使用property属性同名.deleter装饰器装饰的同名方法,提供del xx的方法。

 

另外,重要的一点是既然我们已经使用property来代替实例属性,那自然我们是不希望外层可以直接修改实例属性的,

既然如此我们就最好不能使用上述例子中使用的self.num的命名方式来定义实例属性,而是使用私有属性的方式来定义(关于私有属性的知识点可以查看文章开头的相关知识点):

 1 class Num:
 2 
 3     def __init__(self):
 4         self.__num = 1
 5 
 6     @property
 7     def x(self):
 8         return self.__num
 9 
10     @x.setter
11     def x(self, item):
12         if isinstance(item, int):
13             self.__num = item
14         else:
15             print('不要把非int类型赋值给x!!!')
16 
17     @x.deleter
18     def x(self):
19         if self.__num:
20             del self.__num
21         else:
22             print('属性不存在')
23 
24     @property
25     def y(self):
26         return 2
27 
28     def printf(self):
29         print(f'{self.x}加{self.y}的和是:{self.x+self.y}')
30 
31 a = Num()  # 把C实例化,命名为a
32 a.__name = 3  # 修改私有属性为3
33 a.printf()

这样外部就不能通过 a.__name = 3的方式来改变私有属性__name,如果需要修改,就要通过a.x = 3的“API”来修改,从而我们可以对“API入参”进行校验和处理。

 

 

 

posted @ 2020-06-11 13:43  KikuBruce  阅读(119)  评论(0)    收藏  举报