Python 装饰器的典型使用场景(2)

本文介绍了Python装饰器作为代理和上下文提供者的两种典型应用场景。代理装饰器用于控制函数访问权限,通过验证用户角色实现安全保护;上下文装饰器则确保函数在正确的环境中运行,并在前后执行必要的操作。

Python 装饰器的典型使用场景(2)


前一篇文章中(http://blog.youkuaiyun.com/qq_26886929/article/details/54091962),我们谈了装饰器的两方面用法,本文将讨论装饰器用作代理(Proxy)和上下文提供者(Context Provider)两个用途:


1. 代理(Proxy)装饰器

代理装饰器可被用作标记和注册函数的全局机制。例如,根据当前用户,保护对代码进行访问的安全层,可以使用一个关联着可调用对象所要求的权限的集中检测器来实现:

class User(object):
    def __init__(self, roles):
        self.roles = roles

class Unauthorized(Exception):
    pass

def protect(role):
    def _protect(function):
        def __protect(*args, **kw):
            user = globals().get('user')
            if user is None or role not in user.roles:
                raise Unauthorized("I won't tell you")
            return function(*args, **kw)
        return __protect
    return _protect

这种模式常常被用在 Python 网络框架中,来定义可发布类的安全。例如,Django 使用装饰器来提供函数访问的安全。

下面是个例子,在这个例子中当前用户被保存在一个全局变量中。当方法被调用时,装饰器会检查该用户的角色,以觉得该用户是否有访问权限:

>>> tarek = User(('admin', 'user'))
>>> bill = User(('user',))
>>> class MySecrets(object):
...     @protect('admin')
...     def waffle_recipe(self):
...         print('use tons of butter!')
...
>>> these_are = MySecrets()
>>> user = tarek
>>> these_are.waffle_recipe()
use tons of butter!
>>> user = bill
>>> these_are.waffle_recipe()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in wrap
__main__.Unauthorized: I won't tell you

2. 上下文提供者(Context Provider)

上下文装饰器被用来确保函数在正确的上下文环境运行,或者确保在函数运行前和运行后执行一些代码。换言之,该装饰器进行设置或取消设置某个特定的运行环境。例如,当一条数据要在多个线程之间共享时,就需要使用锁(lock)来确保数据被保护,以防出现并发访问。锁能够像下面这样放在装饰器中:

from threading import RLock
lock = RLock()
def synchronized(function):
    def _synchronized(*args, **kw):
        lock.acquire()
        try:
            return function(*args, **kw)
        finally:
            lock.release()
    return _synchronized

@synchronized
def thread_safe(): # make sure it locks the resource
    pass

上下文装饰器,通常被上下文管理器(with 语句)取代。


<think>好的,用户想了解Python装饰器的常见使用场景和示例。首先,我需要回忆一下装饰器的基本概念,确保自己理解正确。装饰器本质上是一个函数,它接受另一个函数作为参数,并返回一个新的函数,这样可以动态地扩展被装饰函数的功能,而不需要修改其原始代码。 接下来,根据用户提供的引用资料,特别是引用[2]和[3],装饰器使用场景包括日志记录、性能监控、权限校验、缓存等。我需要把这些场景具体化,给出实际的例子,让用户更容易理解。 首先,日志记录。这是装饰器最常见的应用之一,比如记录函数的执行时间和参数。例如,可以创建一个装饰器,在函数调用前后打印日志信息。这需要编写一个装饰器函数,内部定义一个包装函数,在调用原函数前后添加日志语句。 然后是性能监控,比如计算函数运行时间。这里可能需要用到time模块,记录函数开始和结束的时间,计算差值。需要注意的是,时间计算的准确性,以及如何处理可能出现的异常情况。 权限校验也是一个重要场景,比如在Web开发中检查用户是否登录。装饰器可以检查请求中是否有有效的session或cookie,如果没有则重定向到登录页面。这里可能需要结合具体的框架,比如Flask或Django,但为了通用性,可以举一个简化的例子。 缓存方面,可以使用装饰器来缓存函数的结果,避免重复计算。例如,使用functools.lru_cache,或者自己实现一个简单的缓存机制,将参数和结果存储起来,下次相同参数调用时直接返回结果。需要解释LRU缓存的概念,以及如何应用。 另外,引用[4]提到装饰器和闭包的区别,虽然用户的问题没有直接问这个,但可能在回答中提到它们的关系,比如装饰器通常使用闭包来实现,但两者的目的不同,装饰器用于扩展功能,闭包用于保存状态。 用户还要求每个示例都有代码,所以需要确保代码正确,并且用中文注释。同时,按照用户的系统级指令,数学公式要用$...$,但这个问题可能涉及不多,不过如果有涉及到算法复杂度,比如缓存的时间复杂度,可能需要用公式,比如$O(1)$。 需要检查是否有遗漏的场景,比如输入验证、重试机制、路由注册(在Web框架中)等,但根据引用资料,主要应集中在用户提到的几个常见场景。此外,要确保示例代码符合Python语法,没有错误,比如在权限校验的例子中使用模拟的检查函数。 最后,生成相关问题,参考用户给的示例,问题应围绕装饰器的其他应用、原理、与闭包的区别、注意事项等。例如,如何创建带参数的装饰器,或者装饰器的执行顺序问题。 现在开始组织回答,先概述装饰器的概念,然后分点列出场景,每个场景一个示例代码,并解释代码的作用。最后附上相关问题。需要确保引用标记正确,比如在引用日志记录时加上[^2],性能监控引用[^3]等。</think>### Python装饰器的常见使用场景及示例 装饰器Python中用于动态扩展函数/类功能的语法糖,通过`@decorator`语法在不修改原代码的前提下添加新功能。以下是5个典型应用场景及代码示例: --- #### 1. 日志记录(函数行为追踪) ```python def log_decorator(func): def wrapper(*args, **kwargs): print(f"[LOG] 开始执行: {func.__name__},参数: {args} {kwargs}") result = func(*args, **kwargs) print(f"[LOG] 执行完成: {func.__name__}") return result return wrapper @log_decorator def calculate_sum(a, b): return a + b # 调用示例 calculate_sum(3, b=5) # 输出执行日志 ``` **作用**:自动记录函数调用细节,用于调试和审计[^2] --- #### 2. 性能监控(耗时分析) ```python import time def timeit_decorator(func): def wrapper(*args, **kwargs): start_time = time.perf_counter() result = func(*args, **kwargs) elapsed = time.perf_counter() - start_time print(f"耗时 {elapsed:.6f} 秒") return result return wrapper @timeit_decorator def process_data(data_size): return sum(range(data_size)) # 调用示例 process_data(1000000) # 输出执行时间 ``` **应用场景**:性能调优时定位耗时操作 --- #### 3. 权限校验(访问控制) ```python def login_required(func): def wrapper(user, *args, **kwargs): if not user.is_authenticated: raise PermissionError("需要登录后访问") return func(user, *args, **kwargs) return wrapper class User: def __init__(self, auth): self.is_authenticated = auth @login_required def view_profile(user): print("显示用户个人资料") # 调用示例 guest = User(auth=False) view_profile(guest) # 触发权限异常 ``` **典型应用**:Web框架中的路由保护 --- #### 4. 缓存加速(避免重复计算) ```python from functools import lru_cache @lru_cache(maxsize=128) def fibonacci(n): if n < 2: return n return fibonacci(n-1) + fibonacci(n-2) # 调用示例 print(fibonacci(50)) # 递归计算但自动缓存结果 ``` **优势**:将时间复杂度从$O(2^n)$降低到$O(n)$ --- #### 5. 错误重试(容错处理) ```python import random from time import sleep def retry(max_attempts=3, delay=1): def decorator(func): def wrapper(*args, **kwargs): for attempt in range(max_attempts): try: return func(*args, **kwargs) except Exception as e: print(f"尝试 {attempt+1} 次失败: {str(e)}") sleep(delay) raise Exception("所有重试均失败") return wrapper return decorator @retry(max_attempts=5, delay=2) def unstable_api_call(): if random.random() < 0.8: # 模拟80%失败率 raise ConnectionError("API请求失败") return "成功" # 调用示例 print(unstable_api_call()) ``` **适用场景**:网络请求、数据库操作等非确定性操作 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值