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

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

admin2个月前 (03-22)Python67

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 教程] Matplotlib 数据可视化教程

Matplotlib 数据可视化教程 Matplotlib 是 Python 最常用的绘图库。本文介绍常用图表的绘制方法。 一、基础设置 import matplotlib.pyplot as pl...

Python 装饰器的高级应用与实战技巧

装饰器本质上是接受函数作为参数并返回新函数的高阶函数。理解这一点是掌握装饰器的关键。让我们从基础开始,逐步深入到高级应用。首先,我们需要理解函数在 Python 中是一等公民。这意味着函数可以像其他对...

Python 装饰器实战:从基础到高级应用的完整指南

装饰器是 Python 中最优雅也最强大的特性之一。它允许你在不修改原函数代码的前提下,动态地添加功能。本文将带你从装饰器的基础概念出发,逐步掌握其在实际开发中的高级应用技巧。许多初学者对装饰器感到困...

Python 上下文管理器:不只是 with 语句那么简单

在 Python 编程中,上下文管理器(Context Manager)是一个被低估的强大工具。大多数开发者只知道用 with open() 来安全地处理文件,但实际上,上下文管理器的应用场景远不止于...

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

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

Python 装饰器从入门到实战:5 个实用场景详解

装饰器(Decorator)是 Python 中一种优雅的设计模式,它允许我们在不修改原函数代码的前提下,动态地给函数添加功能。想象一下,你有一个已经写好的函数,现在想给它添加日志记录、性能监控或权限...

发表评论

访客

看不清,换一张

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