Python装饰器

103 阅读3分钟

Python装饰器

在Python中,装饰器 Decorator 是一种用于修改函数或者方法的强大工具。下面以统计方法执行时间为例,来介绍Python装饰器的基本用法。

1 基本装饰器实现

以下是一个简单的装饰器示例,用于统计方法的执行时间:

import time

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Execution time: {end_time - start_time} seconds")
        return result
    return wrapper


# 使用装饰器
@timing_decorator
def my_function():
    time.sleep(1)
    print("Function executed")


# 调用被装饰的函数
my_function()

使用 functools.wraps 保留原函数信息

在上述示例中,装饰器会修改原函数的元信息,例如函数名、文档字符串等。为了保留原函数的元信息,可以使用 functools.wraps 装饰器:

import time
import functools import wraps

def timing_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Execution time: {end_time - start_time} seconds")
        return result
    return wrapper

@timing_decorator
def my_function():
    """This is my function"""
    time.sleep(1)
    print("Function executed")

print(my_function.__name__)  # 输出: my_function
print(my_function.__doc__)  # 输出: This is my function

3 带参数的装饰器

装饰器也可以接受参数,例如下面的示例中,可以指定是否打印函数执行时间:

import time
from functools import wraps

def timing_decorator_with_params(format_string="{func_name} took {time:.4f} seconds"):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            print(format_string.format(func_name=func.__name__, time=end_time - start_time))
            return result
        return wrapper
    return decorator

@timing_decorator_with_params(format_string="{func_name} executed in {time:.6f} seconds")
def my_function():
    time.sleep(2)
    print("Function executed.")

# 调用被装饰的函数
my_function()

4 类方法中使用装饰器

装饰器也可以用于类方法,例如下面的示例中,用于统计类方法的执行时间:

import time
from functools import wraps

def timing_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        execution_time = end_time - start_time
        print(f"Method '{func.__name__}' took {execution_time:.4f} seconds to execute.")
        return result
    return wrapper

class MyClass:
    @timing_decorator
    def my_method(self):
        time.sleep(2)
        print("Method executed.")

# 创建类实例并调用方法
obj = MyClass()
obj.my_method()

5 使用contextlib实现上下文管理器

除了使用装饰器,还可以使用 contextlib 模块中的 contextmanager 装饰器来实现上下文管理器,例如下面的示例中,用于统计代码块的执行时间:

import time
from contextlib import contextmanager

@contextmanager
def timing_context():
    start_time = time.time()
    yield
    end_time = time.time()
    execution_time = end_time - start_time
    print(f"Code block took {execution_time:.4f} seconds to execute.")

# 使用上下文管理器
with timing_context():
    time.sleep(2)
    print("Code block executed.")

装饰器用于实例方法的注意事项

在使用装饰器装饰实例方法时,需要注意装饰器的参数列表中是否包含 self 参数。如果装饰器的参数列表中不包含 self 参数,那么在装饰实例方法时,需要在装饰器内部手动传入 self 参数。

  • self 参数:实例方法的第一个参数是 self,表示实例对象本身。装饰器在调用被装饰方法时,需要确保传递 self 参数。
  • 方法绑定:装饰器返回的函数或方法需要正确绑定到实例对象。Python 会自动处理这一点,但如果你手动调用装饰器返回的函数,需要确保传递正确的参数。

总结

  • 装饰器:通过装饰器可以方便地统计函数的执行时间,而无需修改函数本身的代码。
  • functools.wraps:用于保留被装饰函数的元信息。
  • 参数化装饰器:可以通过参数化装饰器来定制输出格式或其他行为。
  • 类方法装饰器:装饰器也可以用于类方法。
  • 上下文管理器:使用 contextlib 可以统计代码块的执行时间。