python中decorator的用法及原理(一)

本文详细介绍了Python中的装饰器概念,包括不带参数的装饰器、带参数的装饰器及其内部工作原理。此外还探讨了装饰器如何应用于类成员函数及多个装饰器的叠加使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0、 概念

什么叫装饰器,其实也可以叫做包装器。即对于一个既有的函数func(args),在调用它之前和之后,我们希望都做一些事情,把这个函数包装起来。

python中的装饰器分为两类:函数装饰器和类装饰器。

这里我们先讨论函数装饰器。

1. 不带参数的decorator

(1) 基本用法:

def decorator1(func):
    def dec(*args):
        print 'pre action'
        result = func(*args)
        print 'post action'
        return result
    return dec

@decorator1
def test_f1(name):
    print name
    return None

test_f1('name1') #out: preaction/name1/post action
test_f1('name2') #out: preaction/name2/post action

(2) 这种现象的内部原理:

在python内部,当你做了这件事情:

@decorator1
def test_f1(name):

其实就是 test_f1 = decorator1(test_f1) #即test_f1作为参数传递给func。

此后的test_f1是装饰器中的dec函数对象了,而不是原来的函数的名称。当调用test_f1(‘name1’)的时候,其实调用的是dec(‘name1’)函数,而在dec函数内部,又调用了func,这样就造成了装饰器的效果。

这也解释了func是被装饰函数,*arg是被装饰函数的参数—这种现象了。


2. 带参数的decorator

(1) 基本用法:

def wap(name):
    def decorator1(func):
        def dec(*args):
            print name
            print 'pre action'
            result = func(*args)
            print 'post action'
            return result
        return dec
    return decorator1

@wap('f1')
def test_f1(name):
    print name
    return None

@wap('f2')
def test_f2(name):
    print name
    return None

test_f1('name1') #out: f1/pre action/name1/post action
test_f1('name2') #out: f2/pre action/name2/post action

带参数的decorator,作用是通过传递参数可以定制不同的装饰器。


(2) 内部原理

这里和上面 不带参数的decorator类似,

@wap('f1')
def test_f1(name):

内部逻辑为: test_f1 = wap(‘f1’)(test_f1)

这里wap(‘f1’)返回是decorator1函数对象,这样的话,wap(‘f1’)(test_f1)其实就是decorator1(test_f1),这样就和上面的一样了。只不过这里传递了一个参数’f1’进入decorator内部,使得我们可以操作这个参数。


3. 函数decorator也可以修饰类成员函数

class FOO:
    @decorator1
    def fun(self):
        print self.name

注意此时fun的self会被传递到decorator1中。此时把self看做普通的函数入参。


4. 函数decorator的叠加

(1) 用法

def decorator1(func):
    def dec(*args):
        print 'd1 pre'
        result = func(*args)
        print 'd1 post'
        return result
    return dec

def decorator2(func):
    def dec(*args):
        print 'd2 pre'
        result = func(*args)
        print 'd2 post'
        return result
    return dec

@decorator1
@decorator2
def test(name):
    print name

test('test') #out: d1 pre/d2 pre/test/d1 post/d2 post

(2) 原理

@decorator1
@decorator2
def test(name):
    print name

和上面的类似,内部原理是:

test = decorator1(decorator2(test))

注意decorator1(decorator2(test)),不是说先执行decorator2(test),再执行decorator1。

而是先把decorator2(test)作为参数,最先执行decorator1,然后再执行decorator2.。


参考文章:http://thecodeship.com/patterns/guide-to-python-function-decorators/

<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]。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值