Python高阶函数装饰器

“ 从CANoe vTESTstudio版本7开始,支持使用python编辑器编写python脚本。其中CANoe提供了许多API接口给python使用,大大扩展了python的可用性。在python中使用装饰器定义capl中的事件处理程序(on key/on timer等)。对此我们有必要了解什么是装饰器”

 

装饰器,装饰是包装的意思,器表示工具。所以装饰器字面意思指的是包装用的工具。就像是买的礼物外面的包装盒子一样

可以看出,装饰器有三个特点:

  • 不能改变礼物的本身
  • 包装盒和礼物是一起的
  • 拿出礼物时只会说礼物的名字,不会说包装盒的名称

“ 我们以给别人买生日蛋糕为例,你让服务员给蛋糕包装时,肯定不能让包装盒破坏蛋糕本身;你每次把蛋糕拿出来给别人看时,包装盒必定和蛋糕是一起拿出来的;当你拿出蛋糕时,只会介绍说这是蛋糕,并不会说这是我买的蛋糕和包装盒,对吧!” 

那么在python中蛋糕和包装盒分别表示什么呢?蛋糕就是python函数,包装盒就是装饰器

所以,装饰器的特定是:

  • 不能改变函数的内部代码
  • 调用函数时装饰器一并调用
  • 使用函数名调用函数

我们定义一个函数并运行:

def func1():
    print("run func1")
    
func1()

 打印的结果为:run func1

现在我想给它添加点特色,就是在打印“run func1”前先打印“program start”。有人说那我把func1函数体改成这样:

def func1():
    print("program start")
    print("run func1")

func1()   

但是如此一来,就破坏了函数func1的函数体,这肯定不是装饰器的作用

那这样呢?

def func1():
    print("run func1")

print("program start")
func1()  

这样也不符合调用函数func1时一并调用装饰器,因为上面的代码实际上调用了print("program start")和func1(),肯定也不是装饰器的作用

那我利用函数的可参数化呢?

def func1():
    print("run func1")

def prog1(func):
    print("program start")
    func()

prog1(func1) 

这样使用的是其他函数调用的,也不符合装饰器的特点

分析:不能改变函数func1的结构,肯定得把函数func1当作参数传入另一个函数prog1中,在另一个函数中实现在调用func1前调用print("program start")。但是又不能通过调用prog1实现,还是要调用func1。怎么办?可以把prog1赋值给func1,以此实现调用func1即是调用prog1

下面这样可以吗?

def func1():
    print("run func1")

def prog1(func):
    print("program start")
    func()

func1 = prog1(func1)

func1()

运行脚本后你会发现报错,报错内容如下:

出问题的点在哪呢?就在func1 = prog1(func1),当把调用prog1(func1)返回的对象赋值给func1时,调用prog1(func1)其实并没有返回值。那么func1就变成了None,最后调用func1肯定就报错了

所以需要在函数prog1的函数体内return一个对象,这个对象需要赋值给func1。由于func1是函数指针,那么prog1 return的对象也应该是一个函数指针。怎么办?可以在prog1函数体内再包一层函数,把这个函数指针返回

def func1():
    print("run func1")

def prog1(func):
    def wrapfunc():
        print("program start")
        func()
    return wrapfunc

func1 = prog1(func1)

func1()  

函数prog1这样的结构就是一个装饰器,使用时只需要把被装饰的函数指针传func1传参给prog1,然后再赋值给func1,如此调用func1就会连装饰器一起调用

但是func1 = prog1(func1)这样的写法麻烦且不具有共通性,所以python提供了一种装饰器的标准用法

 

def prog1(func):
    def wrapfunc():
        print("program start")
        func()
    return wrapfunc

@prog1
def func1():
    print("run func1")

func1() 

prog1是装饰器函数,func1是被装饰的函数,只需要在定义被装饰的函数前面用@符号引出装饰器函数名称,就实现了把被装饰的函数指针传给调用的装饰器函数然后赋值给被装饰函数指针的功能

结束了吗?并没有!

如果你觉得最后调用的func1还是以前的func1就错了,不信可以打印一下它的名称:

print(func1.__name__)  

打印结果:wrapfunc

为什么?因为func1 = prog1(func1)时,调用prog1(func1)返回的就是wrapfunc函数,然后又赋值给了func1,所以func1已经不是之前的func1了。它重写了函数的名字和注释文档

有解决的办法吗?有!!!

python提供了一个函数functools.wraps解决这个问题

from functools import wraps

def prog1(func):
    @wraps(func)
    def wrapfunc():
        print("program start")
        func()
    return wrapfunc

@prog1
def func1():
    print("run func1")

func1() 

@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性

总结:

  • 装饰器本质上是函数,需要编写
  • 函数只有加上后面的括号才是调用,只有函数名,是函数指针
  • 函数指针可以作为参数传参
  • 函数指针作为对象可以赋值给其他的变量
  • 函数体内可以定义其他的函数
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五行缺你94

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值