Python类装饰器全面指南:提升代码效率与可维护性

举报
frica01 发表于 2024/01/22 10:17:51 2024/01/22
【摘要】 本文详细介绍几种在 Python 类中常用的装饰器,包括它们的作用、应用场景以及基本用法。这些知识点不仅有助于深化对 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

子类必须实现 areaperimeter 方法,否则会抛出 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 的结果,所以后续不再进行计算,直接返回结果。
101 次调用 1002 次调用 1003 次调用 1004 次调用 1005 次调用 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 及其变体(gettersetterdeleter)、@classmethod@staticmethod@abstractmethod,以及 functools.lru_cache。这些装饰器各有其特点和用途,它们可以帮助我们创建更加强大、灵活且易于维护的类。理解并合理运用这些装饰器,将使你能够更有效地利用 Python 的面向对象编程特性,编写出更加清晰、高效且符合设计原则的代码。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。