Skip to content

Python 装饰器

装饰器是 Python 中一种强大的功能,用于修改函数或类的行为。本章节将详细介绍 Python 中的装饰器概念、实现原理和使用方法。

基本概念

装饰器是一个函数,它接受一个函数作为参数,并返回一个新的函数。装饰器的作用是在不修改原函数代码的情况下,为函数添加额外的功能。

装饰器的语法使用 @ 符号,放在函数定义的上方。

基本语法

python
@decorator_function
def original_function():
    # 函数体

这相当于:

python
def original_function():
    # 函数体

original_function = decorator_function(original_function)

简单装饰器示例

第一个装饰器

python
# 简单装饰器示例

def my_decorator(func):
    def wrapper():
        print("在函数调用之前")
        func()
        print("在函数调用之后")
    return wrapper

@my_decorator
def say_hello():
    print("Hello, World!")

# 调用函数
say_hello()

输出:

在函数调用之前
Hello, World!
在函数调用之后

带参数的函数装饰器

python
# 带参数的函数装饰器

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("在函数调用之前")
        result = func(*args, **kwargs)
        print("在函数调用之后")
        return result
    return wrapper

@my_decorator
def add(a, b):
    return a + b

# 调用函数
result = add(3, 5)
print(f"结果:{result}")

输出:

在函数调用之前
在函数调用之后
结果:8

带参数的装饰器

装饰器本身也可以接受参数。

python
# 带参数的装饰器

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(n):
                print(f"第 {i+1} 次调用")
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello, World!")

# 调用函数
say_hello()

输出:

第 1 次调用
Hello, World!
第 2 次调用
Hello, World!
第 3 次调用
Hello, World!

装饰器的工作原理

装饰器的工作原理可以分为以下几个步骤:

  1. 定义一个装饰器函数,它接受一个函数作为参数。
  2. 在装饰器函数内部定义一个包装函数(wrapper),用于添加额外的功能。
  3. 包装函数调用原函数,并返回其结果。
  4. 装饰器函数返回包装函数。
  5. 使用 @ 语法将装饰器应用到目标函数上。

示例解析

python
# 装饰器的工作原理解析

def my_decorator(func):
    print(f"装饰器被应用到函数 {func.__name__}")
    
    def wrapper(*args, **kwargs):
        print(f"调用函数 {func.__name__} 之前")
        result = func(*args, **kwargs)
        print(f"调用函数 {func.__name__} 之后")
        return result
    
    return wrapper

@my_decorator
def say_hello(name):
    print(f"Hello, {name}!")
    return f"Hello, {name}!"

print("\n调用函数之前")
result = say_hello("Alice")
print(f"函数返回值:{result}")

输出:

装饰器被应用到函数 say_hello

调用函数之前
调用函数 say_hello 之前
Hello, Alice!
调用函数 say_hello 之后
函数返回值:Hello, Alice!

保留函数元数据

当使用装饰器时,原函数的元数据(如函数名、文档字符串等)会丢失,因为装饰器返回的是包装函数。为了保留原函数的元数据,我们可以使用 functools.wraps 装饰器。

示例

python
# 保留函数元数据

import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        """包装函数的文档字符串"""
        print("在函数调用之前")
        result = func(*args, **kwargs)
        print("在函数调用之后")
        return result
    return wrapper

@my_decorator
def say_hello():
    """打印问候语"""
    print("Hello, World!")

# 查看函数元数据
print(f"函数名:{say_hello.__name__}")
print(f"文档字符串:{say_hello.__doc__}")
print(f"模块名:{say_hello.__module__}")

# 调用函数
say_hello()

输出:

函数名:say_hello
文档字符串:打印问候语
模块名:__main__
在函数调用之前
Hello, World!
在函数调用之后

不使用 functools.wraps 的情况

python
# 不使用 functools.wraps 的情况

def my_decorator(func):
    def wrapper(*args, **kwargs):
        """包装函数的文档字符串"""
        print("在函数调用之前")
        result = func(*args, **kwargs)
        print("在函数调用之后")
        return result
    return wrapper

@my_decorator
def say_hello():
    """打印问候语"""
    print("Hello, World!")

# 查看函数元数据
print(f"函数名:{say_hello.__name__}")
print(f"文档字符串:{say_hello.__doc__}")
print(f"模块名:{say_hello.__module__}")

# 调用函数
say_hello()

输出:

函数名:wrapper
文档字符串:包装函数的文档字符串
模块名:__main__
在函数调用之前
Hello, World!
在函数调用之后

多个装饰器

一个函数可以应用多个装饰器,装饰器的应用顺序是从下到上(或从右到左)。

示例

python
# 多个装饰器

import functools

def decorator1(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("装饰器 1 开始")
        result = func(*args, **kwargs)
        print("装饰器 1 结束")
        return result
    return wrapper

def decorator2(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("装饰器 2 开始")
        result = func(*args, **kwargs)
        print("装饰器 2 结束")
        return result
    return wrapper

@decorator1
@decorator2
def say_hello():
    print("Hello, World!")

# 调用函数
say_hello()

输出:

装饰器 1 开始
装饰器 2 开始
Hello, World!
装饰器 2 结束
装饰器 1 结束

类装饰器

除了函数装饰器,Python 还支持类装饰器。类装饰器是一个类,它实现了 __call__ 方法,用于包装函数。

示例

python
# 类装饰器

class MyDecorator:
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        print("在函数调用之前")
        result = self.func(*args, **kwargs)
        print("在函数调用之后")
        return result

@MyDecorator
def say_hello():
    print("Hello, World!")

# 调用函数
say_hello()

输出:

在函数调用之前
Hello, World!
在函数调用之后

带参数的类装饰器

python
# 带参数的类装饰器

class Repeat:
    def __init__(self, n):
        self.n = n
    
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            for i in range(self.n):
                print(f"第 {i+1} 次调用")
                result = func(*args, **kwargs)
            return result
        return wrapper

@Repeat(3)
def say_hello():
    print("Hello, World!")

# 调用函数
say_hello()

输出:

第 1 次调用
Hello, World!
第 2 次调用
Hello, World!
第 3 次调用
Hello, World!

装饰器的应用场景

装饰器在以下场景中特别有用:

  1. 日志记录:记录函数的调用时间、参数和返回值。
  2. 性能分析:测量函数的执行时间。
  3. 权限验证:验证用户是否有权限执行函数。
  4. 缓存:缓存函数的执行结果,避免重复计算。
  5. 异常处理:捕获并处理函数执行过程中的异常。
  6. 事务管理:确保函数执行的原子性。

日志记录示例

python
# 日志记录装饰器

import functools
import datetime

def logger(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        timestamp = datetime.datetime.now().isoformat()
        print(f"[{timestamp}] 调用函数 {func.__name__},参数:{args}, {kwargs}")
        result = func(*args, **kwargs)
        print(f"[{timestamp}] 函数 {func.__name__} 返回:{result}")
        return result
    return wrapper

@logger
def add(a, b):
    return a + b

@logger
def say_hello(name):
    return f"Hello, {name}!"

# 调用函数
add(3, 5)
say_hello("Alice")

输出:

[2024-01-01T00:00:00.000000] 调用函数 add,参数:(3, 5), {}
[2024-01-01T00:00:00.000000] 函数 add 返回:8
[2024-01-01T00:00:00.000000] 调用函数 say_hello,参数:('Alice',), {}
[2024-01-01T00:00:00.000000] 函数 say_hello 返回:Hello, Alice!

性能分析示例

python
# 性能分析装饰器

import functools
import time

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        duration = end_time - start_time
        print(f"函数 {func.__name__} 执行时间:{duration:.4f} 秒")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)
    return "Done"

@timer
def fast_function():
    return "Done"

# 调用函数
slow_function()
fast_function()

输出:

函数 slow_function 执行时间:1.0001 秒
函数 fast_function 执行时间:0.0000 秒

权限验证示例

python
# 权限验证装饰器

import functools

def requires_permission(permission):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 模拟用户权限
            user_permissions = ["read", "write"]
            
            if permission in user_permissions:
                print(f"权限验证通过,执行函数 {func.__name__}")
                return func(*args, **kwargs)
            else:
                print(f"权限验证失败,缺少 {permission} 权限")
                return None
        return wrapper
    return decorator

@requires_permission("read")
def read_data():
    return "读取数据"

@requires_permission("write")
def write_data():
    return "写入数据"

@requires_permission("delete")
def delete_data():
    return "删除数据"

# 调用函数
print(read_data())
print(write_data())
print(delete_data())

输出:

权限验证通过,执行函数 read_data
读取数据
权限验证通过,执行函数 write_data
写入数据
权限验证失败,缺少 delete 权限
None

缓存示例

python
# 缓存装饰器

import functools

def cache(func):