当前位置:首页 > Python > 正文内容

Python 装饰器进阶:从原理到实战的完整指南

admin2周前 (03-22)Python20

Python 装饰器是 Python 语言中最强大、最优雅的特性之一。它允许你在不修改函数源代码的情况下,动态地为函数添加额外的功能。本文将从装饰器的基本原理出发,深入探讨带参数的装饰器、类装饰器、装饰器链以及实战应用场景。

一、装饰器的基本原理

装饰器的本质是一个接受函数作为参数,并返回一个新函数的高阶函数。Python 使用 @ 语法糖来让装饰器的使用更加简洁优雅。

让我们从一个最简单的计时装饰器开始:

import time
import functools

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f'函数 {func.__name__} 执行时间: {endend - start:.6f} 秒')
        return result
    return wrapper

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

print(fibonacci(10))

在这个例子中,timer 函数接受被装饰的函数作为参数,返回一个新的 wrapper 函数。当我们调用 fibonacci(10) 时,实际上是在调用 wrapper 函数,它会记录执行时间并调用原始的 fibonacci 函数。

注意:使用 functools.wraps 装饰器可以保留原函数的元信息(如 __name__、__doc__ 等),这对于调试和文档生成非常重要。

二、带参数的装饰器

有时候我们需要让装饰器接受参数,这时需要创建一个装饰器工厂函数。这个工厂函数接受参数,返回一个真正的装饰器。

def repeat(times=2):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(times):
                result = func(*args, **kwargs)
                results.append(result)
            return results[-1] if times > 0 else None
        return wrapper
    return decorator

@repeat(times=3)
def say_hello(name):
    print(f'你好, {name}!')
    return f'问候了 {name}'

say_hello('小明')

这个 repeat 装饰器可以让函数执行指定的次数。这种三层结构(工厂函数 → 装饰器 → wrapper)是带参数装饰器的标准模式。

三、类装饰器

除了函数装饰器,Python 还支持类装饰器。类装饰器必须实现 __call__ 方法,使其实例可以像函数一样被调用。

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0
        functools.update_wrapper(self, func)
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f'调用次数: {self.count}')
        return self.func(*args, **kwargs)

@CountCalls
def process_data(data):
    print(f'处理数据: {data}')
    return data.upper()

print(process_data('hello'))
print(process_data('world'))
print(f'总调用次数: {process_data.count}')

类装饰器的优势是可以维护状态(如本例中的调用计数),这在某些场景下比函数装饰器更方便。

四、装饰器链

Python 允许一个函数应用多个装饰器,装饰器的执行顺序是从下到上(离函数定义最近的装饰器最先执行)。

def log_calls(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f'调用函数: {func.__name__}')
        print(f'参数: args={args}, kwargs={kwargs}')
        result = func(*args, **kwargs)
        print(f'返回值: {result}')
        return result
    return wrapper

def cache_result(func):
    cache = {}
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        key = str(args) + str(kwargs)
        if key not in cache:
            cache[key] = func(*args, **kwargs)
            print('计算并缓存结果')
        else:
            print('从缓存中获取结果')
        return cache[key]
    return wrapper

@log_calls
@cache_result
def expensive_calculation(x, y):
    print('执行复杂计算...')
    time.sleep(0.1)
    return x ** 2 + y ** 2

print(expensive_calculation(3, 4))
print(expensive_calculation(3, 4))
print(expensive_calculation(5, 12))

在这个例子中,log_calls 会记录每次函数调用,而 cache_result 会缓存计算结果以提高性能。装饰器链让我们可以组合多个独立的功能模块。

五、实战应用:权限验证装饰器

在实际项目中,装饰器常用于权限验证、日志记录、性能监控等场景。下面是一个实用的权限验证装饰器:

class AuthenticationError(Exception):
    pass

def require_permission(permission):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(user, *args, **kwargs):
            if not user.get('authenticated', False):
                raise AuthenticationError('用户未登录')
            if permission not in user.get('permissions', []):
                raise AuthenticationError(f'缺少权限: {permission}')
            return func(user, *args, **kwargs)
        return wrapper
    return decorator

@require_permission('delete')
def delete_file(user, filename):
    print(f'删除文件: {filename}')
    return True

admin_user = {
    'authenticated': True,
    'permissions': ['read', 'write', 'delete']
}

guest_user = {
    'authenticated': True,
    'permissions': ['read']
}

delete_file(admin_user, 'test.txt')
try:
    delete_file(guest_user, 'test.txt')
except AuthenticationError as e:
    print(f'错误: {e}')

六、性能优化:装饰器实现缓存装饰器

对于纯函数(相同输入总是产生相同输出),我们可以使用装饰器实现缓存来大幅提升性能:

def memoize(max_size=128):
    def decorator(func):
        cache = {}
        order = []
        
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            key = (args, frozenset(kwargs.items()))
            
            if key in cache:
                order.remove(key)
                order.append(key)
                return cache[key]
            
            result = func(*args, **kwargs)
            
            if len(cache) >= max_size:
                oldest_key = order.pop(0)
                del cache[oldest_key]
            
            cache[key] = result
            order.append(key)
            return result
        
        wrapper.cache_info = lambda: {'hits': len(cache), 'size': max_size}
        return wrapper
    return decorator

@memoize(max_size=100)
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n-1)

import time
start = time.time()
print(factorial(50))
print(f'缓存信息: {factorial.cache_info()}')
print(f'执行时间: {time.time() - start:.6f} 秒')

七、总结

Python 装饰器是一种优雅而强大的代码复用机制,它遵循 DRY(Don't Repeat Yourself)原则,让我们的代码更加简洁和可维护。关键要点:

  • 装饰器本质是高阶函数,接受函数并返回新函数
  • 使用 functools.wraps 保留原函数的元信息
  • 带参数的装饰器需要三层结构(工厂 → 装饰器 → wrapper)
  • 类装饰器适合需要维护状态的场景
  • 装饰器链从下到上执行,可以组合多个功能
  • 实战中常用于权限验证、缓存、日志、性能监控等场景

掌握装饰器将让你的 Python 代码更加 Pythonic,更具表现力和可维护性。

相关文章

[Python 教程] NumPy 数组操作详解

NumPy 数组操作详解 NumPy 是 Python 科学计算的基础库,提供高性能的多维数组对象。本文详细介绍 NumPy 数组的核心操作。 一、创建数组 import numpy as np...

[Python 教程] Python 网络请求与爬虫基础

Python 网络请求与爬虫基础 requests 是 Python 最常用的 HTTP 库。本文介绍网络请求和爬虫的基础知识。 一、基础请求 import requests # GET 请求 r...

Python 装饰器完全指南:从入门到实战

装饰器本质上是一个接受函数作为参数并返回新函数的高阶函数。理解这一点是掌握装饰器的关键。想象一下,你有一个函数,你想在它执行前后添加一些额外的逻辑,比如日志记录、性能测试、权限验证等。装饰器就是为这种...

Python装饰器完全指南:从基础到高级应用

装饰器是 Python 中最强大也最容易被误解的特性之一。很多初学者听说过装饰器,但总是感觉云里雾里,不敢在实际项目中使用。本文从最基础的概念讲起,逐步深入到高级应用场景,通过大量原创示例代码帮助...

深入理解 Python 上下文管理器:从基础到高级应用

Python 的 with 语句和上下文管理器是每个开发者都应该掌握的高级技巧,但很多初学者对它的理解仅仅停留在文件操作层面。本文将深入讲解上下文管理器的原理、多种实现方式,以及在实际开发中的高级应用...

深入理解 Python 装饰器:从基础到高级的完整指南

什么是装饰器?装饰器本质上是一个 Python 函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能。装饰器的返回值通常也是一个函数对象。这种设计模式遵循了"开放封闭原则"——对扩展开放,...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。