Python装饰器(4)带参数的装饰器

内容纯属个人理解,不对之处,欢迎指正。

之前说过,装饰器其实就是函数,既然是函数,那就可以有参数,装饰器也不例外,接下来我们来分析带参数的装饰器。

如何构造带参数

带参数倒是很简单,在装饰的时候给装饰函数写上参数就行,但是具体的装饰器函数该怎么写,我们需要思考一下。
我们想让它带参数,无非就是对被装饰函数的执行进行一定条件的限定设置,也就是说在函数执行之前,函数就必须具备这个条件,那么这样的话,就有两种选择,要么在传递func以后传递参数;要么在传递func之前传递参数。
在想法上,两种其实是都可以的,但是,我们传递参数的时候是给装饰器传递,也就是说,装饰器函数首先接收的是装饰器函数的参数,然后带着这个参数再去装饰函数。因此,我们就必须先处理装饰器函数的参数,然后再去处理被装饰的函数。
所以在实现上,我们应该这样去构造带参装饰器函数:

def deco_para(parameter):
    def deco_func(func):
        def wrapper(*args, **kwargs):
            print(parameter)
            func(*args, **kwargs)
        return wrapper
    return deco_func

带参装饰器示例

def deco_para(parameter):
    print('enter deco_para')

    def deco_func(func):
        print('enter deco_func')

        def wrapper(*args, **kwargs):
            print('enter wrapper')
            print(parameter)
            print('---wrapper: before func---')
            func(*args, **kwargs)
            print('---wrapper: after func---')

        return wrapper

    return deco_func


@deco_para(123)
def foo():
    print('---foo---')


if __name__ == '__main__':
    print('--start--')
    foo()

运行结果:

enter deco_para
enter deco_func
--start--
enter wrapper
123
---wrapper: before func---
---foo---
---wrapper: after func---

结果应该不用多说,先接收参数123,然后接收函数foo,最后执行wrapper。

装饰过程解析--多次输出问题

我们解析下过程

deco_func = deco_para(123)  # 接收参数123
wrapper = deco_func(foo)  # 接收函数foo
foo = wrapper  # 重命名
foo()  # 执行foo

如果你试着按这个过程去执行代码,会发现一个问题,wrapper函数里面的代码执行了2次。

enter deco_para
enter deco_func
enter deco_para
enter deco_func
enter wrapper
123
---wrapper: before func---
enter wrapper
123
---wrapper: before func---
---foo---
---wrapper: after func---
---wrapper: after func---

我们看下究竟是怎么回事。

# 程序执行,扫面到装饰器,执行装饰器函数内部代码
1.enter deco_para
2.enter deco_func

# deco_para(123)接收参数123时执行3
3.enter deco_para

# deco_func(foo)接收函数foo时执行4
4.enter deco_func

# foo()执行foo()
5.enter wrapper
6.123
7.---wrapper: before func---

8.enter wrapper
9.123
10.---wrapper: before func---
11.---foo---
12.---wrapper: after func---

13.---wrapper: after func---

在之前解析装饰器的时候就提到过,函数被装饰以后,就不是原来的函数了,也就是说上面所执行的foo,其实是wrapper。
那么wrapper里面有什么东西呢?

print('enter wrapper')
print(parameter)
print('---wrapper: before func---')
func(*args, **kwargs)
print('---wrapper: after func---')

似乎执行结果应该是

enter wrapper
123
---wrapper: before func---
---foo---
---wrapper: after func---

但是事实上,此时的wrapper里面的func已经不是原来的func。
回顾闭包:引用了自由变量的函数即是一个闭包,这个被引用的自由变量和这个函数一同存在, 即使已经离开了了创造它的环境也不例外
那么可以推测,此时的被装饰函数应该具有了额外的东西,这些东西就是

print('enter wrapper')
print(parameter)
print('---wrapper: before func---')
print('---wrapper: after func---')

由此也就可以知道为什么wrapper里面的代码执行了2次:就是在执行到func(*args, **kwargs)的时候,执行了功能丰富以后的func。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 包(lib)、模块(module) 在Python中,存在包和模块两个常见概念。 模块:编写Python代码的py...
    清清子衿木子水心阅读 3,914评论 0 27
  • 每个人都有的内裤主要功能是用来遮羞,但是到了冬天它没法为我们防风御寒,咋办?我们想到的一个办法就是把内裤改造一下,...
    chen_000阅读 1,404评论 0 3
  • 陆焉识与冯婉喻的故事看完了。我的心有着微微刺痛感。 一份爱情,等到满头白发,等到痛彻心扉,才发现,这,原来就是爱。...
    yayajy阅读 302评论 0 0
  • 今天是个好日子
    薇薇摄影阅读 222评论 0 0
  • 处理结果 mysql_fetch_assoc( ) 返回关联数组 mysql_fetch_row( ) 返回索...
    FKTX阅读 710评论 0 0

友情链接更多精彩内容