Python装饰器原理与实战全解:从基础到高级应用

更新时间:2026-10-10 09:59:33 来源:本站整理 浏览:0

1. 装饰器基础

装饰器是 Python 中一种强大的特性,它允许我们在不修改原函数代码的情况下,增强函数的功能。通过装饰器,我们可以将通用逻辑(如日志、计时、权限检查)从业务函数中分离出来,使代码更加简洁和模块化。

def decorator(func):
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)
        print("After function call")
        return result
    return wrapper

@decorator
def greet(name):
    print(f"Hello, {name}!")

# 调用被装饰的函数
greet("Alice")

在上述示例中,decorator 接收一个函数作为参数,并返回一个新的函数 wrapper。当调用 greet 时,实际上执行的是 wrapper,从而在函数调用前后添加了额外的行为。

2. 带参数的装饰器

2.1 基本带参装饰器

有时我们需要为装饰器传递参数,例如指定重复执行的次数。这可以通过在装饰器外层再包裹一层函数来实现。

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

这里 repeat 是一个装饰器工厂,它接收参数 n 并返回一个装饰器。被装饰的函数会执行 n 次。

2.2 装饰器类

除了函数,我们还可以使用类来实现装饰器。通过实现 __call__ 方法,类的实例可以像函数一样被调用。

class Timer:
    def __init__(self, unit="seconds"):
        self.unit = unit

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            import time
            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()
            duration = end - start
            if self.unit == "milliseconds":
                duration *= 1000
            print(f"Function {func.__name__} took {duration:.2f} {self.unit}")
            return result
        return wrapper

@Timer(unit="milliseconds")
def slow_function():
    import time
    time.sleep(1)
    print("Function executed")

slow_function()

装饰器类允许我们通过实例属性保存状态,使得装饰器更加灵活。

3. 高级装饰器技巧

3.1 保留函数元数据

使用 functools.wraps 可以保留被装饰函数的元数据,如函数名、文档字符串等。这在进行调试和文档生成时非常重要。

import functools

def decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        """Wrapper function"""
        print("Before function call")
        result = func(*args, **kwargs)
        print("After function call")
        return result
    return wrapper

@decorator
def greet(name):
    """Greet someone"""
    print(f"Hello, {name}!")

print(greet.__name__)  # 输出: greet
print(greet.__doc__)   # 输出: Greet someone

3.2 叠加装饰器

多个装饰器可以叠加使用,执行顺序是从下到上(即靠近函数的装饰器先执行)。

def decorator1(func):
    def wrapper(*args, **kwargs):
        print("Decorator 1 before")
        result = func(*args, **kwargs)
        print("Decorator 1 after")
        return result
    return wrapper

def decorator2(func):
    def wrapper(*args, **kwargs):
        print("Decorator 2 before")
        result = func(*args, **kwargs)
        print("Decorator 2 after")
        return result
    return wrapper

@decorator1
@decorator2
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")
# 输出顺序:
# Decorator 1 before
# Decorator 2 before
# Hello, Alice!
# Decorator 2 after
# Decorator 1 after

3.3 装饰器工厂

装饰器工厂是一个函数,它返回一个装饰器。这允许我们根据不同的配置创建不同的装饰器。

def create_decorator(prefix):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"{prefix}: Before function call")
            result = func(*args, **kwargs)
            print(f"{prefix}: After function call")
            return result
        return wrapper
    return decorator

@create_decorator("DEBUG")
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

4. 实际应用场景

4.1 日志记录

装饰器可以方便地为函数添加日志记录功能,帮助开发者追踪函数调用和返回值。

import functools
import logging

logging.basicConfig(level=logging.INFO)

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        logging.info(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"{func.__name__} returned: {result}")
        return result
    return wrapper

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

result = add(1, 2)
print(result)

4.2 缓存

对于计算密集型的函数,可以使用装饰器实现简单的缓存机制,避免重复计算。

import functools

def cache(func):
    cache_dict = {}
    @functools.wraps(func)
    def wrapper(*args):
        if args in cache_dict:
            return cache_dict[args]
        result = func(*args)
        cache_dict[args] = result
        return result
    return wrapper

@cache
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(30))  # 第一次计算较慢
print(fibonacci(30))  # 第二次使用缓存,速度很快

4.3 权限验证

在 Web 应用或 API 中,装饰器可以用于检查用户权限,确保只有授权用户才能执行特定操作。

import functools

def requires_permission(permission):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 假设 current_user 是当前登录用户
            current_user = {"permissions": ["read", "write"]}
            if permission not in current_user["permissions"]:
                raise PermissionError(f"User does not have {permission} permission")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@requires_permission("write")
def create_post(title, content):
    print(f"Creating post: {title}")
    return {"title": title, "content": content}

@requires_permission("admin")
def delete_post(post_id):
    print(f"Deleting post: {post_id}")
    return True

create_post("Hello", "World")  # 成功
# delete_post(1)  # 抛出 PermissionError

4.4 性能分析

装饰器可以用来测量函数的执行时间,帮助开发者识别性能瓶颈。

import functools
import time

def performance(func):
    @functools.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:.4f} seconds")
        return result
    return wrapper

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

slow_function()

5. 最佳实践

  • 使用 functools.wraps:保留被装饰函数的元数据,如函数名、文档字符串等。
  • 保持装饰器简单:装饰器应该专注于单一功能,避免过于复杂。
  • 使用参数化装饰器:对于需要配置的装饰器,使用参数化装饰器可以提高灵活性。
  • 注意装饰器的执行顺序:当使用多个装饰器时,注意它们的执行顺序。
  • 测试装饰器:确保装饰器在各种情况下都能正常工作。
  • 文档化装饰器:为装饰器添加文档字符串,说明其功能和用法。
  • 避免修改函数签名:装饰器应该保持函数的签名不变。

6. 总结

装饰器是 Python 中一种强大的特性,它允许我们以一种简洁、优雅的方式增强函数的功能。通过掌握装饰器的高级应用,我们可以编写更加模块化、可维护的代码。

在实际应用中,装饰器可以用于日志记录、缓存、权限验证、性能分析等多种场景,大大提高代码的复用性和可维护性。

希望本文对你理解和应用 Python 装饰器有所帮助!

原文链接:本站整理

本文来自互联网用户投稿,该文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 service@2cto.cc 进行投诉反馈,一经查实,立即处理!

您可能感兴趣的文章: