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

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

admin4个月前 (03-22)Python116

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 上下文管理器的 5 个实用技巧,让你的代码更优雅

在 Python 编程中,上下文管理器(Context Manager)是一个优雅的资源管理工具。你可能已经熟悉最常见的用法——使用 with 语句打开文件,但上下文管理器的能力远不止于此。今天,我将...

Python 高级技巧:让你的代码更优雅高效

# Python 高级技巧:让你的代码更优雅高效 在 Python 编程的世界里,掌握基础语法只是第一步。真正的高手懂得运用高级技巧,让代码更简洁、更高效、更易维护。今天,我将分享一些实用且不那么广为...

Python 类型提示实战指南:让代码更健壮

类型提示并不是强制执行的类型系统,而是一种可选的代码文档化工具。它通过注解函数参数和返回值的类型,帮助开发者更清晰地表达意图,同时让 IDE 和类型检查器(如 mypy)能够提前发现潜在的类型错误。...

Python 上下文管理器深度解析与实战应用

# Python 上下文管理器深度解析与实战应用 ## 概述 Python 的上下文管理器(Context Manager)是一个非常优雅且强大的特性,它通过 `with` 语句为我们提供了一种自...

Python 装饰器高级实战:从基础到精通的5个实用技巧

引言:为什么要深入掌握装饰器? 装饰器是 Python 中最优雅的元编程工具之一,它能在不修改原函数代码的情况下,动态地增加功能。很多开发者都知道如何使用 @timer 计时或 @cache 缓存,...

Python 装饰器:从原理到实战应用,打造优雅代码

在 Python 编程中,装饰器(Decorator)是一个经常被提及但又容易让初学者困惑的概念。简单来说,装饰器是一种设计模式,它允许我们在不修改原始函数代码的情况下,为函数添加额外的功能。这种"包...

发表评论

访客

看不清,换一张

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