初次认识decorator和@property
Welcome. 在本文中,将详细学习如何使用Python中的decorator和@property。
将会学习的内容:
- 使用decorator的优势。
- 使用@property的优势。
- 装饰器函数的基础知识:它们是什么以及如何与@property关联起来。
- 如何使用@property定义getter、setter和deleter。
Python中decorator的优势
- 在不确定其他代码块对现有函数的调用,不改变现有函数,保证代码可靠稳定,避免修改现有函数所引发的潜在问题。
- 拓展原来函数功能。
Python中@property的优势
property可以视为使用属性的“Pythonic”方式,因为:
- 用于定义property的语法简洁易读。
- 可以完全地访问实例属性,就像是公共属性一样。
- 通过使用@property,可以重复使用property的名字,以避免为getter、setter和deleter创建新名字。
装饰器简介
装饰器函数(decorator function)是一个函数,它能给现有的函数(此“现有的函数”作为参数传递)添加新功能。使用装饰器函数就像是给冰淇淋(代指另一个函数)添加少量的巧克力(代指新功能)。即给现有的函数添加新功能,并且不对现有的函数进行任何更新修改。
1 def decorator(func): 2 print("在装饰器函数内部,修饰:", func.__name__) 3 def new_function(): 4 print("新增功能,随后执行的函数为:", func.__name__) 5 func() 6 return new_function 7 8 @decorator 9 def initial_function(): 10 print("现有函数功能") 11 12 initial_function()
输出结果为:
在装饰器函数内部,修饰: initial_function 新增功能,随后执行的函数为: initial_function 现有函数功能
在不使用装饰器函数时,实现以上功能的代码如下所示:
1 def decorator(func): 2 print("在装饰器函数内部,修饰:", func.__name__) 3 def new_function(): 4 print("新增功能,随后执行的函数为:", func.__name__) 5 func() 6 return new_function 7 8 9 def initial_function(): 10 print("现有函数功能") 11 12 initial_function = decorator(initial_function) 13 initial_function()
这两段代码,实现功能是相同的。装饰器是替代上述代码initial_function = decorator(initial_function)的语法糖,只增加一行代码就可以将现有函数包装到装饰器函数内。
对比两种实现方案,装饰器@decorator就可以理解为替换initial_function = decorator(initial_function)这条语句的实现。
装饰器更加简洁,有两部分组成:先定义用于包装或“装饰”其他函数的装饰器函数;然后立即在被包装函数的定义前面,加上“@”和装饰器函数名。这里的装饰器函数应该以一个函数为形参,返回值也是一个函数。
@property的实际应用
定义一个House类(该类仅定义了实例属性price)。通过House类创建一个house实例
1 class House: 2 def __init__(self, price): 3 self.price = price 4 5 house = House()
这个实例属性price是公有的,因此可以使用点标记法直接访问和修改属性值。
1 # Access value 2 house.price 3 4 # Modify value 5 house.price = 40000
然而,通常我们并不希望用户能直接访问和修改属性值,因此将实例属性设置为私有的,并采用getter和setter方法间接访问和修改私有的实例属性,更新后的代码如下:
class House: def __init__(self, price): self.__price = price def get_price(self): return self.__price def set_price(self, new_price): self.__price = new_price
之前访问该实例属性的代码均需要修改,对应的客户端也需要进行代码修改:
1 # Changed from house.price 2 house.get_price() 3 4 # Changed from house.price = 40000 5 house.set_price(40000)
为了应对以上的情况,@property应运而生,解决了当时直接使用实例属性的代码重构问题。
1 class House: 2 def __init__(self, price): 3 self.__price = price 4 5 @property 6 def price(self): 7 return self.__price 8 9 @price.setter 10 def price(self, new_price): 11 if new_price > 0 and isinstance(new_price, float): 12 self.__price = new_price 13 else: 14 print("Please enter a valid price") 15 16 @price.deleter 17 def price(self): 18 del self.__price
此时,就可以像之前一样访问实例属性。即初始开发时可以采用传统的实例属性,以后可以根据需要随时随地无缝切换为属性,客户端代码则无须改动。
getter
1 house = House(50000.0) # Create instance 2 print(house.price) # Access value
输出结果为:
50000.0
setter
1 house = House(50000.0) # Create instance 2 house.price = 88888.0 # Update value 3 print(house.price)
输出结果为:
88888.0
deleter
1 # Delete the instance attribute 2 del house.price 3 4 # The instance sttribute doesn‘t exist 5 print(house.price)
输出结果为:
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-13-dd54e9ad0090> in <module> 1 # The instance sttribute doesn‘t exist ----> 2 print(house.price) <ipython-input-8-5e2f43c64399> in price(self) 5 @property 6 def price(self): ----> 7 return self.__price 8 9 @price.setter AttributeError: ‘House‘ object has no attribute ‘_House__price‘
总结
- 使用decorator和@property可以使得Python代码简洁易读。
- @property可以被视为定义getter、setter和deleter的"pythonic"方式。
- 在不影响程序的前提下修改类的内部实现,从而避免直接对数据的访问和修改。
参考文献
- https://www.freecodecamp.org/news/python-property-decorator/
- 《Python快速入门》(第三版)
原文地址:https://www.cnblogs.com/qiuhuachuan/p/12118940.html