Python类装饰器全面指南:提升代码效率与可维护性
Python类装饰器全面指南:提升代码效率与可维护性
前言⚡
在 Python
中,装饰器功能非常强大,它们允许程序员修改或增强现有函数和方法的行为,而无需更改其实际代码。
在面向对象编程中,特别是当涉及到类(class
)的定义时,装饰器扮演着至关重要的角色。本文将详细介绍几种在 Python
类中常用的装饰器,包括它们的作用、应用场景以及基本用法。这些知识点不仅有助于深化对 Python
语言特性的理解,还能在实际编程中提高代码的效率和可维护性。
知识点📖
在 Python中
,类(class
)常用的装饰器及其作用可以以如下表格形式展示:
- 这些装饰器大大增强了
Python
类的功能性和灵活性。
装饰器名称 | 作用 |
---|---|
@property |
将方法转换为只读属性,使得可以通过属性而非方法调用的方式来访问值。 |
@<property_name>.getter |
为对应的@property 装饰的属性创建获取(getter )方法,用于获取属性的值。 |
@<property_name>.setter |
为对应的@property 装饰的属性创建设置器(setter )方法,用于修改属性的值。 |
@<property_name>.deleter |
为@property 装饰的属性创建删除器(deleter )方法,用于删除属性。 |
@classmethod |
将方法转换为类方法,这意味着该方法接收类本身(通常命名为cls )作为第一个参数,而不是类的实例。 |
@staticmethod |
将方法转换为静态方法,这种方法既不接收类的实例(self ),也不接收类本身(cls )作为参数。 |
@abstractmethod |
在抽象基类(ABC)中使用,标记一个方法为抽象方法,这意味着在子类中必须实现该方法。 |
代码实现🎈
@property 🏠
@property
是一个装饰器,用于将类中的方法转换为一个看似普通的属性。
- 作用:将类中的方法转换为一个看似普通的属性,但实际上是具有特殊行为的属性。
- 应用场景:用于创建只读属性,或在访问属性时执行某些特定逻辑(如数据格式化、验证等)。
基本用法如下:
class MyClass:
@property
def my_property(self):
...
代码示例:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""
这是一个只读属性,用于获取半径值。
通过属性访问,而不是方法调用。
"""
return self._radius
if __name__ == '__main__':
# 使用属性访问半径值
circle = Circle(5)
print(circle.radius) # 输出: 5
@<property_name>.getter 📖
@property.getter
是一个装饰器,用于定义或重定义一个属性的获取(读取)方法。
- 作用:定义或重定义访问属性时的行为。
- 应用场景:用于自定义获取属性值的过程,例如添加额外操作或修改属性值的返回。
基本用法如下:
class MyClass:
@property
def my_property(self):
...
@my_property.getter
def my_property(self):
...
代码示例:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""
这是一个只读属性,用于获取半径值。
通过属性访问,而不是方法调用。
"""
return self._radius
@radius.getter
def radius(self):
# 确保年龄总是返回正数
print('调用了 getter 方法')
return abs(self._radius)
if __name__ == '__main__':
# 使用属性访问半径值
circle = Circle(5)
print('修改前 ==> ', circle.radius) # 输出: 5
@<property_name>.setter 🔧
@property.setter
是一个装饰器,用于定义或重定义设置属性值时的行为。
- 作用:定义或重定义设置属性值时的行为。
- 应用场景:用于自定义设置属性值的过程,如数据验证、自动转换类型、更新相关属性等。
基本用法如下:
class MyClass:
@property
def my_property(self):
...
@my_property.setter
def my_property(self):
...
示例代码:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""
这是一个只读属性,用于获取半径值。
通过属性访问,而不是方法调用。
"""
return self._radius
@radius.setter
def radius(self, value):
"""
这是一个属性的 setter 方法,用于设置半径值。
可以通过属性设置值,而不需要调用方法。
"""
if not isinstance(value, int):
raise ValueError('radius must be a Int')
self._radius = value
if __name__ == '__main__':
# 使用属性访问半径值
circle = Circle(5)
print('修改前 ==> ', circle.radius) # 输出: 5
circle.radius = 100
print('修改前 ==> ', circle.radius)
circle.radius = ''
这里,尝试给 name
赋非字符串值时会引发 ValueError
。
输出:
修改前 ==> 5
修改前 ==> 100
Traceback (most recent call last):
raise ValueError('radius must be a Int')
ValueError: radius must be a Int
@<property_name>.deleter 🗑️
@property.deleter
是一个装饰器,用于定义删除属性时的行为。
- 作用:定义删除属性时的行为。
- 应用场景:在删除属性时执行清理操作,如释放资源、通知其他组件等。
基本用法如下:
class MyClass:
@property
def my_property(self):
...
@my_property.deleter
def my_property(self):
...
示例代码:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""
这是一个只读属性,用于获取半径值。
通过属性访问,而不是方法调用。
"""
return self._radius
@radius.deleter
def radius(self):
"""
这是一个属性的 deleter 方法,用于删除半径值。
可以通过属性删除值,而不需要调用方法。
"""
del self._radius
if __name__ == '__main__':
# 使用属性访问半径值
circle = Circle(5)
print('radius ==>', getattr(circle, 'radius', None))
del circle.radius # 删除半径属性
print('radius ==>', getattr(circle, 'radius', None))
输出:
radius ==> 5
radius ==> None
@classmethod 🏫
@classmethod
是一个装饰器,用于将方法转换为作用域类本身的类方法。
- 作用:将方法转换为类方法,即方法不是作用于实例,而是作用于类本身。这个方法接收类本身作为第一个参数(通常命名为
cls
)。 - 应用场景:用于实现只与类相关,而非与特定实例相关的行为,如工厂方法、基于类的配置等。
它的基本用法如下:
class MyClass:
@classmethod
def class_method(cls, ...):
...
示例代码:
- 对比前面的代码,它直接通过类
Circle
调用这个方法,而无需创建一个Circle
类的实例。
class Circle:
_radius = 100
@classmethod
def radius(cls):
"""
这是一个类方法,用于访问类变量 class_variable 的值。
通过类调用时,可以获取类级别的属性或执行类级别的操作。
"""
return cls._radius
if __name__ == '__main__':
# 使用属性访问半径值
radius = Circle.radius()
print(radius) # 输出 100
@staticmethod ⚙️
@staticmethod
是一个装饰器,用于将方法转换为静态方法。
- 作用:将方法转换为静态方法,即方法既不需要类也不需要实例。
- 应用场景:用于实现与类相关,但不需要类或实例数据的功能,如工具函数。
它的基本用法如下:
class MyClass:
@staticmethod
def static_method(...):
...
可能会有人有疑问,下面代码中的 add
方法定义在外面,作用也是一样的? 为什么需要定义在 class
类 里面呢?
优点如下:
- 组织性和可读性:将相关的功能方法组织在类内部,使代码更易于理解和维护;
- 命名空间隔离:将方法定义在类内部可以将其限制在类的命名空间内,避免全局命名冲突。这有助于避免与其他模块或库中的函数或变量发生命名冲突。
- 面向对象设计原则:在面向对象编程中,方法通常与类相关联,因此将功能方法放在类内部符合面向对象设计原则,使代码更具对象化特征。
- 继承和多态:如果你在类内定义方法,这些方法可以通过继承来扩展和覆盖,从而支持多态和子类化。
代码示例:
add
不需要类或实例的上下文。
class Circle:
@staticmethod
def add(x, y):
"""
这是一个静态方法,用于执行加法操作。
它不需要访问实例属性,可以通过类调用。
"""
return x + y
if __name__ == '__main__':
# 使用属性访问半径值
radius = Circle.add(10, 20)
print(radius) # 输出 30
@abstractmethod 📜
@staticmethod
是一个装饰器,用于在抽象基类中声明抽象方法。
- 作用:在抽象基类中声明抽象方法,要求子类必须实现该方法。
- 应用场景:定义一个接口或者抽象基类,要求继承该类的子类必须实现特定的方法。
它的基本用法如下:
from abc import ABC, abstractmethod
class MyAbstractClass(ABC):
@abstractmethod
def my_abstract_method(self):
pass
示例代码:
from abc import ABC, abstractmethod
class Geometry(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
class Circle(Geometry):
def area(self):
return 100
def perimeter(self):
return 30
if __name__ == '__main__':
# 使用属性访问半径值
circle = Circle()
print('面积 ==>', circle.area())
print('周长 ==>', circle.perimeter())
输出:
面积 ==> 100
周长 ==> 30
子类必须实现 area
和 perimeter
方法,否则会抛出 TypeError
错误!
@functools.lru_cache🎉
functools.lru_cache
是一个装饰器,用于为函数提供缓存功能。
- 作用:缓存函数的调用结果,当再次使用相同的参数调用函数时,可以直接返回缓存的结果,而不是重新计算。
- 应用场景:
- 优化性能:对于计算密集型或I/O密集型的函数,缓存可以显著提高性能。
- 重用结果:在递归调用或频繁调用相同参数的情况下特别有用。
基本用法如下:
from functools import lru_cache
@lru_cache(maxsize=None)
def my_function(...):
...
示例代码:
from functools import lru_cache
# 应用 lru_cache 装饰器,设置缓存大小为 100
@lru_cache(maxsize=100)
def square(n):
print(n)
return n * n
if __name__ == '__main__':
for i in range(1, 6):
print(f'第 {i} 次调用', square(10))
输出:
- 可以清晰的看到,因为已经缓存了10 的结果,所以后续不再进行计算,直接返回结果。
10
第 1 次调用 100
第 2 次调用 100
第 3 次调用 100
第 4 次调用 100
第 5 次调用 100
综合案例🧐
代码结合上述的介绍一起看!
from functools import lru_cache
from abc import (ABC, abstractmethod)
class Geometry(ABC):
@abstractmethod
def area(self):
pass
@staticmethod
@lru_cache(maxsize=32)
def add(a, b):
return a + b
@property
def description(self):
return "This is a geometry object."
@description.setter
def description(self, value):
raise ValueError("Cannot modify the description.")
@classmethod
def info(cls):
return f"This is a {cls.__name__} class."
class Circle(Geometry):
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if not isinstance(value, (int, float)) or value <= 0:
raise ValueError("Radius must be a positive number.")
self._radius = value
def area(self):
return 3.14 * self.radius ** 2
if __name__ == '__main__':
# 使用类
circle = Circle(5)
print('面积 ==>', circle.area()) # 计算面积
print('相加 ==>', Circle.add(3, 4)) # 使用缓存的静态方法
print('描述 ==>', circle.description) # 访问属性
print('info ==>', Circle.info()) # 类方法
circle.radius = 10 # 修改属性
print('info ==>', Circle.radius) # 半径
# circle.description = "New Description" # 尝试修改只读属性(将引发错误)
总结🍬
在本文中,我们探讨了多种Python类中常用的装饰器,包括 @property
及其变体(getter
、setter
、deleter
)、@classmethod
、@staticmethod
、@abstractmethod
,以及 functools.lru_cache
。这些装饰器各有其特点和用途,它们可以帮助我们创建更加强大、灵活且易于维护的类。理解并合理运用这些装饰器,将使你能够更有效地利用 Python
的面向对象编程特性,编写出更加清晰、高效且符合设计原则的代码。
- 点赞
- 收藏
- 关注作者
评论(0)