Python中decorator

本文介绍如何使用Python的@decorator来增强函数功能,同时保持原函数的元信息不变。通过functools.wraps可以解决@decorator修改函数名称等问题,确保函数的__name__、__doc__等属性保持不变。

@decorator可以实现函数功能的动态增加,但是,经过@decorator“改造”后的函数,和原函数相比,除了功能多一点外,有没有其它不同的地方?


栗子1:
在没有decorator的情况下,打印函数名:

def f1(x):
    pass
print(f1.__name__)

结果为: f1

栗子2:
在有decorator的情况下,打印函数名:

def log(f):
    def wrapper(*args, **kw):
        print('call...')
        return f(*args, **kw)
    return wrapper

@log
def f2(x):
    pass
print(f2.__name__)

结果为: wrapper

由以上的栗子可见,函数名返回的已经不是原函数 f2 了,而是装饰器函数内部定义的 wrapper ,这对于那些依赖函数名的代码就失效了。
decorator还改变了函数的__doc__等其它属性。如果要让调用者看不出一个函数经过了@decorator的“改造”,就需要把原函数的一些属性复制到新函数中:

def log(f):
    def wrapper(*args, **kw):
        print('call...')
        return f(*args, **kw)
    wrapper.__name__ = f.__name__
    wrapper.__doc__ = f.__doc__
    return wrapper

@log
def f2(x):
    pass
print(f2.__name__)

结果为: f2

However, 这样写decorator很不方便,因为我们也很难把原函数的所有必要属性都一个一个复制到新函数上,所以Python内置的functools可以用来自动化完成这个“复制”的任务:

import functools
def log(f):
    @functools.wraps(f)
    def wrapper(*args, **kw):
        print('call...')
        return f(*args, **kw)
    return wrapper

@log
def f2(x):
    pass
print(f2.__name__)

结果为: f2

任务:
给上节代码加上,@functools.wraps使其原函数的属性不会在引用时发生变化。

import time, functools
from functools import reduce

def performance(unit):
    def perf_decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kw):
            print('call'+' '+'%s() in %s%s' % (f.__name__, time.time(), unit))
        return wrapper
    return perf_decorator

@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

print(factorial.__name__)

结果为: factorial

注意:若括号中无原函数名@functools.wraps(f),将会报错如下:

AttributeError: ‘functools.partial’ object has no attribute ‘name


大家加油!
学习链接:https://www.imooc.com/code/6067

<think>我们正在讨论Python中的装饰器(decorator)。根据用户的问题,他想了解装饰器的用法和定义。我将结合引用中提到的内容,并按照系统级指令的要求进行回答。首先,装饰器是Python中一个强大的功能,它允许用户修改或增强函数的行为而不改变函数本身。装饰器本质上是一个可调用对象(如函数),它接受一个函数作为参数并返回一个新的函数。根据引用[1]:任何可以被调用的、带一个参数的Python对象都可以被当作是一个decorator。但是,我们通常将装饰器分为两类(尽管引用中没有明确说明,但通常分为函数装饰器和类装饰器)。引用[4]中提到了装饰器的优点:增加程序的灵活性,减少耦合度。例如,用户登录检查的功能可以通过装饰器来实现,这样每个需要登录检查的函数只需要加上一个装饰器即可,而无需在每个函数内部调用登录检查函数。这样,当登录检查逻辑需要修改时,只需修改装饰器即可。引用[2]和[3]分别展示了装饰器的定义和使用。其中引用[2]中的例子:```pythondefauth_book(auth=None):print(auth)defdeco(func):defwrapper(*args,**kwargs):res=func(*args,**kwargs)returnresreturnwrapperreturndeco```这是一个带参数的装饰器,它有两层嵌套函数:最外层接受参数,中间层接受被装饰的函数,最内层是包装函数。下面我将详细介绍装饰器的定义和使用。###装饰器的基本概念装饰器本质上是一个函数(或可调用对象),它接受一个函数作为参数,并返回一个新的函数。在返回的新函数中,我们可以添加额外的功能。####简单装饰器示例```pythondefmy_decorator(func):defwrapper():print("在函数执行前做一些事情")func()print("在函数执行后做一些事情")returnwrapper@my_decoratordefsay_hello():print("Hello!")say_hello()```输出:```在函数执行前做一些事情Hello!在函数执行后做一些事情```###带参数的装饰器有时我们希望装饰器本身可以接受参数。此时需要再嵌套一层函数,如下所示:```pythondefrepeat(num_times):defdecorator_repeat(func):defwrapper(*args,**kwargs):for_inrange(num_times):result=func(*args,**kwargs)returnresultreturnwrapperreturndecorator_repeat@repeat(num_times=3)defgreet(name):print(f"Hello{name}")greet("Alice")```输出:```HelloAliceHelloAliceHelloAlice```###类装饰器除了函数装饰器,我们还可以使用类来实现装饰器。类装饰器通过实现`__call__`方法,使得类的实例可以像函数一样被调用。```pythonclassCountCalls:def__init__(self,func):self.func=funcself.num_calls=0def__call__(self,*args,**kwargs):self.num_calls+=1print(f"这是第{self.num_calls}次调用")returnself.func(*args,**kwargs)@CountCallsdefsay_hello():print("Hello!")say_hello()say_hello()```输出:```这是第1次调用Hello!这是第2次调用Hello!```###装饰器的应用场景1.**日志记录**:记录函数的调用信息。2.**性能测试**:测试函数的运行时间。3.**权限校验**:在Web开发中检查用户是否登录或具有权限。4.**缓存**:缓存函数的结果,避免重复计算。5.**事务处理**:确保数据库操作的事务性。###注意事项-装饰器会改变被装饰函数的元信息(如函数名、文档字符串等)。可以使用`functools.wraps`来保留这些元信息。-装饰器的顺序:多个装饰器从下往上应用(靠近函数的装饰器先执行,然后依次向外)。###使用functools.wraps为了保留原始函数的元信息,我们可以在定义装饰器时使用`functools.wraps`。```pythonimportfunctoolsdefmy_decorator(func):@functools.wraps(func)defwrapper(*args,**kwargs):print("装饰器操作")returnfunc(*args,**kwargs)returnwrapper@my_decoratordefexample():"""这是文档字符串"""passprint(example.__name__)#输出:exampleprint(example.__doc__)#输出:这是文档字符串```###总结装饰器是Python中一种高级功能,它允许我们在不修改原函数代码的情况下增加函数的功能。通过嵌套函数和闭包,我们可以实现各种复杂的装饰器。正确使用装饰器可以使代码更加简洁、灵活,并降低耦合度。§§相关问题§§1.装饰器如何保留被装饰函数的元信息?2.多个装饰器作用于同一个函数时,它们的执行顺序是怎样的?3.装饰器在哪些实际项目中被广泛应用?4.如何实现一个类装饰器?5.装饰器能否装饰类?如何实现?</think>### Python装饰器(Decorator)的用法与定义 装饰器(Decorator)是Python的核心特性之一,**用于动态修改函数或类的行为**,本质上是一个接受函数作为参数并返回新函数的高阶函数。它通过`@decorator_name`语法实现,主要应用于代码复用、增强功能或分离关注点。 #### 一、基本定义与原理 1. **核心结构**: ```python def decorator(func): # 接收被装饰函数 def wrapper(*args, **kwargs): # 定义新行为 # 增强逻辑 (如日志、权限检查) result = func(*args, **kwargs) # 调用原函数 # 后处理逻辑 return result return wrapper # 返回新函数 @decorator def target_function(): # 原函数逻辑 ``` - **执行顺序**:当调用`target_function()`时,实际执行的是`decorator(target_function)`返回的`wrapper`函数。 2. **简单示例**(日志记录): ```python def log_execution(func): def wrapper(*args, **kwargs): print(f"执行函数: {func.__name__}") return func(*args, **kwargs) return wrapper @log_execution def add(a, b): return a + b print(add(2, 3)) # 输出: 执行函数: add → 5 ``` #### 二、关键应用场景 1. **权限验证**(引用[4]案例): ```python def login_required(func): def wrapper(user, *args, **kwargs): if not user.is_authenticated: raise PermissionError("需登录") return func(user, *args, **kwargs) return wrapper @login_required def view_profile(user): return f"用户资料: {user.name}" ``` - **优势**:避免在每个函数中重复登录检查代码,降低耦合度[^4]。 2. **性能监控**: ```python import time def timeit(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print(f"{func.__name__}耗时: {time.time()-start:.2f}s") return result return wrapper ``` #### 三、进阶用法 1. **带参数的装饰器**(引用[2]案例): ```python def auth_book(auth_type): # 外层接收参数 def decorator(func): # 中层接收函数 def wrapper(*args, **kwargs): if auth_type == "admin": print("管理员权限校验") return func(*args, **kwargs) return wrapper return decorator @auth_book(auth_type="admin") def delete_book(book_id): print(f"删除图书: {book_id}") ``` 2. **保留元信息**: 使用`functools.wraps`避免被装饰函数丢失`__name__`等属性: ```python from functools import wraps def decorator(func): @wraps(func) # 保留原函数元信息 def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper ``` 3. **类装饰器**: ```python class DecoratorClass: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print("类装饰器前置操作") return self.func(*args, **kwargs) @DecoratorClass def example(): pass ``` #### 四、设计原则与注意事项 1. **单一职责**:每个装饰器只完成一个明确任务(如日志、权限、缓存)。 2. **避免副作用**:确保装饰器不影响原函数的输入/输出行为。 3. **调试技巧**:复杂装饰器链可通过断点检查`wrapper`函数的执行流。 > **总结**:装饰器通过高阶函数实现行为的动态扩展,其核心价值在于**分离核心逻辑与横切关注点**(如日志、权限),显著提升代码可维护性和复用性[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值