python3-三个demo带你入门装饰器

装饰器入门

在不修改程序源代码和程序调用方式的情况下,扩展程序功能时不得不用到装饰器。

python中的装饰器可谓功能强大,强大到刚接触它就被它弄得措手不及。

但是,静下心来好好研究,那可是回味无穷。于是,整理了下面2个demo,归纳总结。

demo1:简单装饰器

#demo1
def w(func):
    def inner():
        print("begin...")
        func()
        print("end...")
    return inner

@w              #语法糖:@w
def test():
    print("我就是那个被装饰的函数")
    
test()
#output:
begin...
我就是那个被装饰的函数
end...

这就是一个简单的装饰器。所谓装饰指的是在test函数功能的基础上增加一些别的功能,本demo中用两次打印(即:begin...\end...)代替,且调用的方式不变还是原来的 test()。

初看demo1你应该是不明白到底发生了什么,那个@w是什么意思。

其实,@w 放在test函数的定义上面,这种简单的语法是python装饰器的一种简写。这样显的简单大气上档次。

但是,最原始、最本质的其实是高阶函数的使用,涉及到知识点有:

  • 函数可以当参数传给另一个参数
  • 函数可以当返回值被返回给一个变量,调用该变量就相当于调用被返回的函数

说了这么多,还是不如直接看代码来的快。

#demo1本质
def w(func):
    print("我要开始装饰啦...")
    def inner():
        print("begin...")
        func()
        print("end...")
    return inner


def test():
    print("我就是那个被装饰的函数")
    
test=w(test)    #本质所在
test()

你看,test=w(test) 这行语句是多么强大,玩的多么巧妙。

首先,我们应该知道这行语句执行的本质:

  1. 把test函数名当实参传给w函数,传给w函数中的形参func,
  2. 另外,w函数中嵌套定义了inner函数,func在inner中被调用,被装饰的test函数在inner中被调用,
  3. 但是 inner函数在w中没有被调用而是当返回值被返回到w函数外。接收的变量是test,此处的test仅仅是以一个变量名,虽然和需要被装饰的函数test长的一模一样,但本质是不一样的。
  4. 然后,程序的最后调用 test(),本质不是调用需要被装饰的那个test函数,而是调用了w函数的返回的函数 inner。
  5. 于是,直到现在被装饰的函数test,才在inner调用被执行中间调用。

这就是装饰的全部过程。总结:使用test=w(test)之后,调用test(),就相当于调用了inner()。

所以现在明白了demo1的output输出结果的顺序了吗?

因为首先调用的是w函数,然后遇到w函数返回inner函数,再开始调用inner函数。

现在回头看,@w放在test函数的定义前,其实很简单很方便地隐藏了私底下那些“见不得人”的操作。

不过这样的好处是显而易见的,官方取名为 语法糖,简单形象。

demo2:demo1补充

为了加深装饰器的本质执行流程,还有必要再唠叨两句,直接看代码。

#demo2:
def w(func):
    
    print("我要开始装饰啦....")    #w中第一条语句
    def inner():                #w中第二条语句
        print("begin...")
        func()
        print("end...")
        
    print("好了装饰完毕了吗?")    #w中第三条语句
    return inner

@w              #语法糖:@w
def test():
    print("我就是那个被装饰的函数")
    
test()

你猜,输出结果会是神马样子,是你想的那个样子吗?

我还是直接说吧,其实我在代码中加了注释的。

我们知道,装饰器的本质执行语句是:test=w(test)

  • 首先,是执行w函数,那就从上往下,分别还行语句1、语句2、语句3。。。
  • 所以,首先执行的是语句1,打印 :我要开始装饰啦....
  • 然后,语句2(其实这样说不准确),但是语句2是函数的定义,在w中并未被调用,所以此时并不会有输出,
  • 再其次是语句3,打印:好了装饰完毕了吗?
  • 于是w函数执行完毕了,遇到了return返回,返回的是inner函数名给test这个变量名
  • 最后的最后,调用test加(),即执行inner函数,于是打印:begin... 等

所以打印结果如下:

#output:
我要开始装饰啦....
好了装饰完毕了吗?
begin...
我就是那个被装饰的函数
end...

demo3:两个装饰器

一个函数被两个装饰器装饰,是检验你是否理解装饰器的好东西。

#demo3:
def w1(func):
    print("我是w1")
    def inner():
        print("w1,begin...")
        func()
        print("w1,end...")
    return inner

def w2(func):
    print("我是w2")
    def inner():
        print("w2,begin...")
        func()
        print("w2,end...")
    return inner

@w2
@w1
def test():
    print("我是那个被装饰的函数")
    
test()

像上面的思路一样,我们来捋一遍。

demo3中两个语法糖叠加的效果等同于:test=w2(w1(test))

因为两个装饰器w1、w2里面都有参数func和inner函数,可能容易混乱。

所以,下面叙述中提到的func1指的是w1函数的func,inner2指的是w2的inner,其他类似。

#1 按照从左到右的执行顺序,test=w2(w1(test)),首先是把需要被装饰的test函数当实参传给w1函数中的形参func1;
#2 w1函数就会执行,执行相关语句知道遇到return语句返回inner1,此时外面还有一个装饰器,inner1就不是直接返回给一个变量名,而是返回给w2当参数,即func2接收实参inner1;
#3 w2函数就会执行,执行相关语句知道遇到return语句返回inner2,给变量test;
#4 调用加括号的test,就相当于调用inner2(),此时inner2中的func2是inner1,因为#2中func2接收的实参是inner1呀;
#5 当func2加括号调用时,就是调用inner1,此时inner1中的func1是需要被装饰的test,因为#1中func1接收的实参是test呀。


总结:
    调用test()执行步骤如下:
    step1:执行w1,func1形参接收实参test,开始执行语句,先输出“我是w1”,再返回inner1给w2
    step2:执行w2,func2形参接收实参inner1,开始执行语句,先输出“我是w2”,再返回inner2给test变量
    step2:执行test(),就是执行inner2(),于是输出“w2,begin”,调用func2(),此即调用inner1,
    step3:输出“w1.begin”,再调用func1,即调用需要被装饰的test函数,输出“我是那个被装饰的函数”,
    step4:然后依次输出,“w1,end”
    step5:此时inner2()中的func2执行完毕,再执行print("w2,end..."),输出“w2,end...”。
#output:
我是w1
我是w2
w2,begin...
w1,begin...
我是那个被装饰的函数
w1,end...
w2,end...

总结:

#test(w2(w1(test)))
test()
  • 多个装饰器装饰,靠近被装饰函数的先执行
  • w1装饰器中:func是被装饰的函数test,
  • w2装饰器中:func是w1的返回值inner,
  • 最后,调用 变量test(),即执行w2的inner()

转载于:https://www.cnblogs.com/liuxu2019/p/11430835.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值