Python进阶教程1——装饰器

1.什么是装饰器

  • 装饰器就是对被装饰的对象(函数、类)进行重构的,其可以在不改变原来对象的情况下调用对象时执行重构后的行

  • 把一个函数当作参数,返回一个替代版的函数,本质就是一个返回函数的函数

  • 在不改变原函数的基础上,给函数增加功能

  • 对修改是封闭的,对扩展是开放的

      	##引题:当登陆某系统时常常会有欢迎语,但修改时将在登陆函数中修改,可能会出现问题,所以避免直接侵入原函数修改。
      def login():
       print("中秋快乐")
        print("login....")
       print("欢迎您下次光临....")
      login()
      ##为了避免在大的函数块中操作,将欢迎语独立出来,另建函数。
      def desc(fun):
      def add_info():
      print("国庆快乐")
      fun()
      print("欢迎您下次光临")
       return add_info
      def login():
       print("login....")
      login = desc(login)
      login()
    

运行结果:

在这里插入图片描述

2.装饰器的语法糖

在Python中,可以使用”@”语法糖来精简装饰器的代码,把 decorator 置于函数的定义处,免去给函数重新赋值( 即function = decorator(funtion))
在这里插入图片描述
在这里插入图片描述
当原函数有参数时,装饰器同样需要传参,*arges, * * kwargs令装饰器具有一般性
在这里插入图片描述
在这里插入图片描述

3.装饰的函数有返回值

示例
装饰器实现一个函数计时器
1.问题1:被装饰的函数有返回值
在这里插入图片描述
在这里插入图片描述
如上。我们发现装饰的函数有返回值,所以在使用装饰器的时候无法调用函数执行的结果,此时我们可以通过给返回值赋值来调用

在这里插入图片描述
在这里插入图片描述

4.保留被装饰的函数的函数名和帮助文档信息

导入functools标准库,使用functools.wraps函数,保留被装饰的函数的函数名和帮助文档信息。
如下执行结果,没有引入functools.wraps函数前,我们只能查看装饰器的函数名,无法保留被装饰函数的函数名及帮助文档
在这里插入图片描述
在这里插入图片描述
引入之后,就可以保留被装饰函数的函数名及帮助文档
在这里插入图片描述
在这里插入图片描述

5.装饰器中不同条件下执行不同的函数

示例
用户登陆
在这里插入图片描述
在这里插入图片描述

6.多个装饰器

若有两个装饰器,从上到下调用装饰器,wrapper内容也是为由上向下执行

装饰器函数的执行顺序是分为(被装饰函数)定义阶段和(被装饰函数)执行阶段的,装饰器函数在被装饰函数定义好后立即执行

在函数定义阶段:执行顺序是从最靠近函数的装饰器开始,自内而外的执行
在函数执行阶段:执行顺序由外而内,一层层执行

在这里插入图片描述
上面代码先定义里两个函数: decotator_a(), decotator_b(), 这两个函数实现的功能是

接收一个函数作为参数然后返回创建的另一个函数
在这个创建的函数里调用接收的函数

最后,定义的函数 f 采用上面定义的 decotator_a, decotator_b 作为装饰函数。在当我们以1为参数调用装饰后的函数 f 后, decotator_a, decotator_b 的顺序是什么呢?
如果不假思索根据自下而上的原则来判断地话,先执行 decorator_b 再执行 decorator_a , 那么会先输出 Get in decotator_a, Get in inner_a 再输出 Get in decotator_b , Get in inner_b 。然而事实并非如此。
实际上运行的结果如下:
在这里插入图片描述
f函数和函数调用的区别
为什么是先执行 inner_b 再执行 inner_a 呢?为了彻底看清上面的问题,得先分清两个概念:函数和函数调用。

上面的例子中 f 称之为函数, f(1) 称之为函数调用
后者是对前者传入参数进行求值的结果
在Python中函数也是一个对象,所以 f 是指代一个函数对象,它的值是函数体本身
f(1) 是对函数的调用,它的值是调用的返回值

同样地,拿上面的 decorator_a 函数来说,它返回的是个函数对象 inner_a ,这个函数对象是它内部定义的。在 inner_a里调用了函数 func ,将 func 的返回值返回。装饰器函数在被装饰函数定义好后立即执行
其次得理清的一个问题是,当装饰器装饰一个函数时,究竟发生了什么。

当解释器执行这段代码时, decorator_a 已经调用了
它以函数 f 作为参数, 返回它内部生成的一个函数inner_a,所以此后 f 指代的是 decorater_a 里面返回的 inner_a 。
所以当以后调用 f 时,实际上相当于调用 inner_a ,传给 f 的参数会传给 inner_a
在调用 inner_a 时会把接收到的参数传给 inner_a 里的 func 即 f
最后返回的是 f 调用的值,所以在最外面看起来就像直接再调用 f 一样。

疑问的解释

当理清上面两方面概念时,就可以清楚地看清最原始的例子中发生了什么

当解释器执行下面这段代码时,实际上按照从下到上的顺序已经依次调用了 decorator_a 和 decorator_b ,
这时会输出对应的 Get in decorator_a 和 Get in decorator_b 。
这时候 f 已经相当于 decorator_b 里的 inner_b
但因为 f 并没有被调用,所以 inner_b 并没有调用,以此类推 inner_b 内部的 inner_a 也没有调用
所以 Get in inner_a 和 Get in inner_b 也不会被输出。

然后最后一行当我们对 f 传入参数1进行调用时, inner_b 被调用了
它会先打印 Get in inner_b ,然后在 inner_b 内部调用了 inner_a
所以会再打印 Get in inner_a, 然后再 inner_a 内部调用的原来的 f
并且将结果作为最终的返回。

7.装饰器的参数

示例
编写装饰器required_types, 条件如下:
1). 当装饰器为@required_types(int,float)确保函数接收到的,每一个参数都是int或者float类型;
2). 当装饰器为@required_types(list)确保函数接收到的每一个参数都是list类型;
3). 当装饰器为@required_types(str,int)确保函数接收到的每一个参数都是str或者int类型;
4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类型
在这里插入图片描述

在这里插入图片描述
如果需要返回函数的话,带参数的装饰器就要写三层内嵌函数
带参数的装饰器具体执行过程分为两步:首先执行required_types(*kinds),不管中间过程,函数返回的是函数required_int(fun)的内存地址,
此时就变成了@required_int( ),按照不带参数的装饰器的调用过程,此时required_int( )将函数add( )当做是参数执行required_int( )里面的函数wrapper( )。另外,现在wrapper( )不仅有add( ),还有required_types(kinds)本身所携带的参数kinds

带参数的装饰器函数其实就是在原来的基础上添加外层函数,又返回内层的装饰器函数,相当于用多的一层外层函数用来接受内层装饰器函数的参数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值