> 文章列表 > Python常见装饰器使用(实用向)

Python常见装饰器使用(实用向)

Python常见装饰器使用(实用向)

目录

    • 1.`@staticmethod`
    • 2.`@classmethod`
    • 3、`@classmethod` 与`@staticmethod`比较
    • 4. `@property`
    • 5.`@abstractmethod`
    • 6.`@wraps`
    • 7.`@lru_cache`
    • 8.`@timeout`
    • 9.`@retry`
  1. @staticmethod:将一个方法转换为静态方法,可以在不创建类实例的情况下调用。
  2. @classmethod:将一个方法转换为类方法,可以在不创建类实例的情况下调用,并且可以访问类的属性和方法。
  3. @property:将一个方法转换为属性,可以像访问属性一样访问方法,而不需要使用括号调用。
  4. @abstractmethod:定义一个抽象方法,子类必须实现该方法才能被实例化。
  5. @wraps:将一个函数的元信息(如函数名、文档字符串、参数列表等)复制到另一个函数中,可以避免装饰器对函数元信息的影响。
  6. @lru_cache:使用 LRU 算法实现的缓存装饰器,可以缓存函数的计算结果,避免重复计算。
  7. @timeout:设置函数的最大执行时间,如果函数执行时间超过设定的时间,就会抛出 TimeoutError 异常。
  8. @retry:在函数执行失败时自动重试,可以设置重试次数、重试间隔等参数。

这些装饰器都是 Python 中常用的装饰器,可以帮助我们简化代码、提高效率、增强程序的可读性和可维护性。

1.@staticmethod

class MyClass:@staticmethoddef my_static_method(x, y):return x + y# 不需要创建类实例,直接调用静态方法
result = MyClass.my_static_method(1, 2)
print(result)  # 输出 3

2.@classmethod

class MyClass:class_var = 0@classmethoddef my_class_method(cls, x):cls.class_var += xreturn cls.class_var# 不需要创建类实例,直接调用类方法
result1 = MyClass.my_class_method(1)
result2 = MyClass.my_class_method(2)
print(result1, result2)  # 输出 1 3

3、@classmethod@staticmethod比较

classmethodstaticmethod 都是 Python 中的装饰器,用于定义类方法和静态方法。虽然它们都可以在类中定义方法,通过类名直接调用,而不需要创建类的实例,但是它们有一些不同之处。

不同点:

  1. classmethod 的第一个参数是类本身,通常命名为 cls,而 staticmethod 没有特殊的参数。
  2. classmethod 可以访问和修改类的属性和方法,而 staticmethod 只能访问类的属性和方法,不能修改它们。
  3. classmethod 可以被子类继承和重写,而 staticmethod 不会被继承和重写。

以下是一个简单的示例代码,演示了如何使用 classmethodstaticmethod

class MyClass:class_variable = 0def __init__(self, instance_variable):self.instance_variable = instance_variable@classmethoddef class_method(cls):cls.class_variable += 1print("Class variable:", cls.class_variable)@staticmethoddef static_method():print("Static method")# 调用类方法
MyClass.class_method()  # 输出 Class variable: 1# 创建类实例
obj = MyClass(10)# 调用静态方法
obj.static_method()  # 输出 Static method# 调用类方法
obj.class_method()  # 输出 Class variable: 2

4. @property

将一个方法转换为属性,可以像访问属性一样访问方法,而不需要使用括号调用。

我们使用 @property 装饰器定义了一个 getter 方法 x,用于获取私有属性 _x 的值。我们使用 @x.setter 装饰器定义了一个 setter 方法 x,用于设置私有属性 _x 的值。

class MyClass:def __init__(self, x):self._x = x@propertydef x(self):return self._x@x.setterdef x(self, value):self._x = value# 访问属性 x,实际上调用的是方法 x()
obj = MyClass(1)
print(obj.x)  # 输出 1# 修改属性 x,实际上调用的是方法 x(value)
obj.x = 2
print(obj.x)  # 输出 2

5.@abstractmethod

abstractmethod 是 Python 中的一个装饰器,用于定义抽象方法。抽象方法是一种没有实现的方法,它只是一个接口,需要在子类中实现。抽象方法通常用于定义一个类的接口,而不是具体的实现。

使用 abstractmethod 装饰器可以让我们在抽象类中定义抽象方法,强制要求子类实现这些方法。如果子类没有实现抽象方法,那么在实例化子类时会抛出 TypeError 异常。

以下是一个简单的示例代码,演示了如何使用 abstractmethod 装饰器定义抽象方法:

from abc import ABC, abstractmethodclass Shape(ABC):@abstractmethoddef area(self):passclass Rectangle(Shape):def __init__(self, width, height):self.width = widthself.height = heightdef area(self):return self.width * self.heightclass Circle(Shape):def __init__(self, radius):self.radius = radiusdef area(self):return 3.14 * self.radius ** 2# 创建 Rectangle 和 Circle 对象,并调用 area() 方法
rect = Rectangle(10, 20)
print("Rectangle area:", rect.area())circle = Circle(5)
print("Circle area:", circle.area())# 创建 Shape 对象,会抛出 TypeError 异常
shape = Shape()
# TypeError: Can't instantiate abstract class Shape with abstract methods area

6.@wraps

wraps 是 Python 中的一个装饰器,用于修饰其他装饰器,它可以帮助我们保留被修饰函数的元信息,例如函数名、文档字符串、参数列表等。如果我们不使用 wraps 装饰器,那么被修饰函数的元信息可能会丢失或被修改,导致代码可读性和可维护性下降。

使用 wraps 装饰器的主要用途是在编写装饰器时,保留被修饰函数的元信息。例如,如果我们编写一个装饰器,用于记录函数的执行时间,那么使用 wraps 装饰器可以保留被修饰函数的函数名、文档字符串、参数列表等信息,使得调试和维护代码更加方便。

以下是一个简单的示例代码,演示了如何使用 wraps 装饰器保留被修饰函数的元信息:

from functools import wraps
import timedef timer(func):@wraps(func)def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)end_time = time.time()print(f"{func.__name__} took {end_time - start_time:.2f} seconds")return resultreturn wrapper@timer
def my_func():"""This is a docstring"""print("Hello, world!")my_func()
print("Function name:", my_func.__name__)
print("Docstring:", my_func.__doc__)
# (env) [hanhandi@VM-33-162-centos ~/hanhan_PythonScripts/装饰器使用]$ python other.py 
# Hello, world!
# my_func took 0.00 seconds
# Function name: my_func
# Docstring: This is a docstring

在上面的代码中,我们定义了一个名为 timer 的装饰器,用于记录函数的执行时间。我们使用 @wraps 装饰器修饰了内部的 wrapper 函数,保留了被修饰函数的元信息。然后,我们定义了一个名为 my_func 的函数,并使用 @timer 装饰器修饰它。最后,我们调用了 my_func() 函数,并输出了它的执行时间、函数名和文档字符串。

使用 wraps 装饰器可以让我们编写更加健壮和可维护的装饰器,同时也可以提高代码的可读性和可维护性。

7.@lru_cache

这个之前有讲解过,不过之前用的是cachetools:

https://blog.csdn.net/qq_42604176/article/details/130045268?spm=1001.2014.3001.5501

from functools import lru_cache@lru_cache(maxsize=128)
def fibonacci(n):if n < 2:return nreturn fibonacci(n-1) + fibonacci(n-2)# 缓存函数的计算结果,避免重复计算
result = fibonacci(10)
print(result)  # 输出 55

8.@timeout

timeout 是 Python 中的一个装饰器,用于设置函数的最大执行时间。如果函数在规定的时间内没有执行完毕,那么装饰器会抛出 TimeoutError 异常,中断函数的执行。

使用 timeout 装饰器的主要用途是在编写需要限制执行时间的函数时,保证函数不会一直执行下去,避免出现死循环或长时间阻塞的情况。例如,如果我们编写一个函数,用于从远程服务器下载文件,那么使用 timeout 装饰器可以设置最大下载时间,避免下载过程中出现网络故障或服务器故障导致程序一直阻塞。

以下是一个简单的示例代码,演示了如何使用 timeout 装饰器设置函数的最大执行时间:

import signalclass TimeoutError(Exception):passdef timeout(seconds):def decorator(func):def handler(signum, frame):raise TimeoutError("Function timed out")def wrapper(*args, **kwargs):signal.signal(signal.SIGALRM, handler)signal.alarm(seconds)result = func(*args, **kwargs)signal.alarm(0)return resultreturn wrapperreturn decorator@timeout(5)
def my_func():import timetime.sleep(10)print("Function finished")try:my_func()
except TimeoutError as e:print("Function timed out:", e)

在上面的代码中,我们定义了一个名为 timeout 的装饰器,用于设置函数的最大执行时间。我们首先定义了一个名为 TimeoutError 的异常类,用于在函数超时时抛出异常。然后,我们定义了一个名为 decorator 的装饰器函数,它接受一个函数作为参数,并返回一个新的函数 wrapper。在 wrapper 函数中,我们使用 signal 模块设置了一个定时器,当函数执行时间超过规定的时间时,会抛出 TimeoutError 异常。最后,我们使用 @timeout(5) 装饰器修饰了 my_func 函数,设置了最大执行时间为 5 秒。在调用 my_func() 函数时,如果函数执行时间超过 5 秒,会抛出 TimeoutError 异常。

使用 timeout 装饰器可以让我们编写更加健壮和可靠的函数,避免出现死循环或长时间阻塞的情况。同时,它也可以提高代码的可读性和可维护性。

9.@retry

retry 是 Python 中的一个装饰器,用于在函数执行失败时自动重试。如果函数执行失败,装饰器会等待一段时间后再次执行函数,直到函数执行成功或达到最大重试次数为止。

使用 retry 装饰器的主要用途是在编写需要重试的函数时,保证函数能够在失败后自动重试,避免出现因网络故障、服务器故障或其他原因导致函数执行失败的情况。例如,如果我们编写一个函数,用于从远程服务器下载文件,那么使用 retry 装饰器可以设置最大重试次数和重试间隔时间,避免下载过程中出现网络故障或服务器故障导致程序中断。

以下是一个简单的示例代码,演示了如何使用 retry 装饰器设置函数的重试次数和间隔时间:

import timeclass RetryError(Exception):passdef retry(max_retries, wait_time):def decorator(func):def wrapper(*args, **kwargs):for i in range(max_retries):try:result = func(*args, **kwargs)return resultexcept Exception as e:print(f"Function failed: {e}")if i < max_retries - 1:print(f"Retrying in {wait_time} seconds...")time.sleep(wait_time)raise RetryError(f"Function failed after {max_retries} retries")return wrapperreturn decorator@retry(max_retries=3, wait_time=2)
def my_func():import randomif random.random() < 0.8:raise Exception("Random error")else:print("Function succeeded")try:my_func()
except RetryError as e:print("Function failed:", e)# Function failed: Random error
# Retrying in 2 seconds...
# Function failed: Random error
# Retrying in 2 seconds...
# Function failed: Random error
# Function failed: Function failed after 3 retries

在上面的代码中,我们定义了一个名为 retry 的装饰器,用于设置函数的最大重试次数和重试间隔时间。我们首先定义了一个名为 RetryError 的异常类,用于在函数重试次数达到最大值时抛出异常。然后,我们定义了一个名为 decorator 的装饰器函数,它接受一个函数作为参数,并返回一个新的函数 wrapper。在 wrapper 函数中,我们使用 for 循环和 try/except 语句实现了函数的重试逻辑。如果函数执行失败,会等待一段时间后再次执行函数,直到函数执行成功或达到最大重试次数为止。最后,我们使用 @retry(max_retries=3, wait_time=2) 装饰器修饰了 my_func 函数,设置了最大重试次数为 3 次,重试间隔时间为 2 秒。在调用 my_func() 函数时,如果函数执行失败,会自动重试,直到函数执行成功或达到最大重试次数为止。

使用 retry 装饰器可以让我们编写更加健壮和可靠的函数,避免出现因网络故障、服务器故障或其他原因导致函数执行失败的情况。同时,它也可以提高代码的可读性和可维护性。