python中的装饰器二(带参数的装饰器)

前言

看大佬们写的代码看的我是眼花缭乱,不知所云啊。真是感叹,自己还看都看不懂,人家就能写出来了,人与人之间的差距确实有点大呀,为了能够早些看懂大佬们的代码,继续薅头发吧~
阅读这篇可能需要点装饰器基础知识,可以先参考这篇:python中的装饰器(基础装饰器)

一 被装饰函数带参数

在上篇 python中的装饰器(基础装饰器) 中讲的装饰器和被装饰函数都是没有带任何参数的,在实际编程中往往有很多被装饰函数和装饰器都是带有参数的,所以了解它们的带参数情况是很有必要的。下面先从被装饰函数带参数开始,如下。

下面仍然用函数装饰器实现 :计算函数的运行时间 的功能,如下

#被装饰函数带参数
import time
from functools import wraps

def timer(func):

    @wraps(func)
    def decorate():
        start_time = time.time()
        func()
        end_time = time.time()
        total = end_time - start_time
        print("函数运行时间:", total)

    return decorate

@timer
def func():
    time.sleep(2)

func()

上面就用timer装饰器实现了计算函数运行时间的功能,此时被装饰函数func是没有参数的。
如果把func函数延迟的时间由传入参数决定,那么func函数就变成了下面这样

def func(num):
    time.sleep(num)

此时被装饰函数func 带了一个参数num,那么装饰timer又该如何变动呢?timer的变动如下

#被装饰函数带参数
import time
from functools import wraps

def timer(func):

    @wraps(func)
    def decorate(num):
        start_time = time.time()
        func(num)
        end_time = time.time()
        total = end_time - start_time
        print("函数运行时间:", total)

    return decorate

@timer
def func(num):
    time.sleep(num)

func(3)
>>>
函数运行时间: 3.0008623600006104

与上面相比,这里的装饰器timer 有了些变化:decorate()函数和func()函数都多了一个形参num, 这就是当被装饰函数带有参数时装饰器的写法,装饰函数和被装饰函数也应该有对应的形参变量。

func() 添加对应的形参变量我能理解,因为它本身就是被装饰函数,但是装饰函数 decorate() 为什么也要增加形参变量呢?当时这里想了很久都没有想明白(好菜),网上搜索也没有讲的这么详细的,最后在某天就想着把它拆开一步步分析,分析过程如下

  • 1. @timer 还原就是func=timer(func) ,执行timer的时候,timer首先接受一个func变量,然后执行decorate() 函数,decorate() 函数里面调用func(),也就是被装饰函数本身func(),所以decorate()里面的func也要增加对应的形参变量。
  • 2. 当timer装饰完成后,返回decorate,即func=decorate,当执行func(3)的时候带了一个实参3,执行func(3)就相当于执行decorate(3),所以decorate()自然也得有对应的形参变量来接受,并且提供给decorate()里面的func使用。

标准的装饰器timer写法是下面这样的,以应对被装饰函数func不确定的传参

#被装饰函数带参数
import time
from functools import wraps

def timer(func):

    @wraps(func)
    def decorate(*args, **kargs):
        start_time = time.time()
        func(*args, **kargs)
        end_time = time.time()
        total = end_time - start_time
        print("函数运行时间:", total)

    return decorate

@timer
def func(num):
    time.sleep(num)

#func=timer(func)
func(2)

>>>
函数运行时间: 2.0000545978546143

二 被装饰函数有返回值

当被装饰函数有返回值时,我们可以在装饰函数里面用一个变量接受,如下

#被装饰函数有返回值
import time
from functools import wraps

def timer(func):

    @wraps(func)
    def decorate(*args, **kargs):
        start_time = time.time()
        #用变量res接受func的返回值
        res = func(*args, **kargs)
        end_time = time.time()
        total = end_time - start_time
        print("函数运行时间:", total)

        return res

    return decorate

@timer
def func(num):
    time.sleep(num)

    return num

print("func的返回值:", func(2))

>>>
函数运行时间: 2.000554084777832
func的返回值: 2

上面用变量res接受func的返回值,然后return res 返回给外面用。

三 装饰器带参数

对于有参数的装饰器,我们需要用两层闭包来写,如下

#装饰器带参数
import time
from functools import wraps

def my_timer(parm):

    def timer(func):

        @wraps(func)
        def decorate(*args, **kwargs):
            if parm == 1:
                print("这是func1")
                start_time = time.time()
                func(*args, **kwargs)
                end_time = time.time()
                total = end_time - start_time
                print("函数运行时间:", total)
            elif parm == 2:
                print("这是func2")
                start_time = time.time()
                func(*args, **kwargs)
                end_time = time.time()
                total = end_time - start_time
                print("函数运行时间:", total)

        return decorate

    return timer

@my_timer(1)
def func1():
    time.sleep(1)

@my_timer(2)
def func2():
    time.sleep(2)

func1()
func2()
>>>
这是func1
函数运行时间: 1.0001475811004639
这是func2
函数运行时间: 2.0003774166107178

这里的timer比上面的timer多了一层外层函数my_timer,它的作用就是和timer构成一个闭包,然后timerdecorate构成一个闭包。
在这里插入图片描述

因为装饰函数decorate引用了两个自由变量:parm和func,而它的外层函数timer只能传一个func,timer自然需要外加一层函数来传parm变量,并且timer的外层函数要与timer构成闭包,以让接受的parm变量成为自由变量,从而提供给装饰函数decorate使用。
以上就是装饰器带参数的写法。

四 多个装饰器

有时候一个装饰器还不能满足需求,要多个装饰器一起才能实现,多个装饰器的写法很简单,难的是理解它的执行顺序,如下

#多个装饰器
def deco_one(func):

    print("这是deco_one")
    def deco1(*args, **kwargs):
        print("这是deco1的开始")
        func(*args, **kwargs)
        print("这是deco1的结束")

    return deco1

def deco_two(func):

    print("这是deco_two")
    def deco2(*args, **kwargs):
        print("这是deco2的开始")
        func(*args, **kwargs)
        print("这是deco2的结束")

    return deco2

@deco_one
@deco_two
def func():
    print("Hello world")

func()

先说下多个装饰器的执行顺序:

  • 1.不附加在装饰函数里面的是自底向上执行
  • 2.在装饰函数里面的,但在被装饰函数func()之前的是自顶向下执行
  • 3.执行被装饰函数func()
  • 4.在装饰函数里面的,并且在被装饰函数func()之后的是自底向上执行

所以上面的执行结果是

>>>
这是deco_two
这是deco_one
这是deco1的开始
这是deco2的开始
Hello world
这是deco2的结束
这是deco1的结束

在这里插入图片描述

python装饰器带参数的就到此结束了,上篇文章:python中的装饰器(基础装饰器)

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Python装饰器是一种用于修改函数行为的函数。而参数装饰器就是在普通装饰器的基础上,给装饰器传递额外的参数。这样可以在装饰器内部使用这些参数来改变装饰函数的行为。 在给装饰器传递参数时,需要在装饰器函数上面再包裹一层函数来接收参数,并返回装饰器函数。例如,示例代码的@wrapper_out('QQ')就是一个参数装饰器。 当函数执行到参数装饰器@wrapper_out('QQ')这句时,会先执行wrapper_out('QQ')这个函数,将参数'QQ'传给parameter并得到返回值wrapper函数。然后,再将@符号与wrapper函数结合,得到一个标准的装饰器,按照装饰器的执行流程来装饰函数。 通过参数装饰器,可以在装饰器内部对被装饰函数进行进一步的处理或设置,从而实现更灵活的函数装饰。这对于需要根据不同的参数来选择不同装饰方式的情况非常有用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [python参数装饰器](https://blog.csdn.net/weixin_44799217/article/details/118695357)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [python如何修改装饰器参数](https://download.csdn.net/download/weixin_38570854/12870003)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值