Python高级编程——7.装饰器

一、装饰器

装饰器是程序开发中经常会用到的一个功能,用好了装饰器,开发效率如虎添翼。装饰器的功能,就是在运行代码原来功能基础上,加上一些其它功能,比如权限的验证,比如日志的记录等等。不修改原来的代码,进行功能的扩展,使代码的功能更强大。比如java中的动态代理,python的注解装饰器

写代码要遵循开放封闭原则(OCP),虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

· 封闭:已实现的功能代码块

· 开放:对扩展开发

假设已经写好的某段代码如下,该代码的功能就是增加一个用户,只需调用函数即可运行:


运行结果不用多说,自然是成功的增加一个用户:


但若是此时上级要求你,让你把对该段代码的操作记录制作成日志,你肯定会这么做:



这种做法虽然达到了添加日志的目的,但是违背了OPC原则,在原来的代码上做了修改,这是编程中不允许的,因为原来的稳定的代码是被测试过的,后期随意的改动会导致整个代码段不可用,这是禁止的行为。即便是将添加日志的操作写成函数添加在目标函数中,也是不允许的。并且以上的做法会导致工作量加大,且不利于维护。这时就用到了装饰器。


在这里用到的@record就是一个装饰器。为了更好的展示,我们把日志内容print以下,以便看效果。在这里装饰器作用为:在一个函数上方添加了装饰器,如@record,会改变函数代码的运行顺序,装饰器可以引导代码运行直至达到我们想要的效果。以上的例子中,用户在操作时与之前一样只是调用了addUser()函数,在函数即将运行时检测到上方有一个装饰器,此时将会先运行装饰器同名的函数体,在该函数内我们可以写任意需要的代码,原来的函数addUser()的名称addUser将作为参数,传给装饰器函数。在装饰器函数内部嵌套一个函数,以便将原来的代码addUser()函数重新调用,然后返回内部函数的地址,形成闭包(闭包主要是为了解决被装饰函数的参数传递问题,后面会介绍)。这样就使得我们增加日志的需求得以满足,并且不影响原来函数的运行,也不会对用户产生影响。

二、 多个装饰器

如果某函数上面有多个装饰器,此时函数运行时先运行离函数最近的装饰器对应的函数,依次往上运行。请看下面的例子:

#定义函数:完成包裹数据

def makeBold(fn):

    def wrapped():

        return "<b>" + fn() + "</b>"

    return wrapped

#定义函数:完成包裹数据

def makeItalic(fn):

    def wrapped():

        return "<i>" + fn() + "</i>"

    return wrapped

@makeBold

def test1():

    return "hello world-1"

@makeItalic

def test2():

    return "hello world-2"

@makeBold

@makeItalic

def test3():

    return "hello world-3"

print(test1()))

print(test2()))

print(test3()))

运行结果:

<b>hello world-1</b>

<i>hello world-2</i>

<b><i>hello world-3</i></b>

三、 装饰器(decorator)的功能

1. 引入日志

2. 函数执行时间统计

3. 执行函数前预备处理

4. 执行函数后清理功能

5. 权限校验等场景

6. 异常处理

7. 缓存


三、 装饰器(decorator)的示例

1.为无参函数装饰,上面已经作为例子介绍过了,简单看一下图例:

运行结果
2.为有参函数装饰,即需要装饰的函数带有参数,此时可以在装饰器对应的函数中的内部函数传参:



运行结果:


3.被装饰函数有不定长参数,用万能参数 *args,**kwargs:



运行结果:



4.装饰器中的return



5.类装饰器

装饰器函数其实是这样一个接口约束,它必须接受一个callable(可调用)对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。若是某个类的对象重写(overide) __call__() 方法,那么这个对象就是callable的。

先定一个人的类,然后为其实例化一个对象:



该对象被调用后报错:

 File "D:/Project/lianxi/__fibonacci.py", line 14, in <module>
    p1()
TypeError: 'Person' object is not callable  也就是说p1不是一个可以被调用的对象。但若是在类内部重写(overide)__call__()方法后,当前类的所有对象都可以被调用,并且在调用时默认调起__call__()方法。


 
这时我们再执行一次,发现p1可以被调用了,并且还打印出了我们在__call__()方法内部写的一句话: 

也就是说,通过在类内部重写(overide)__call__()方法,我们可以直接调起当前类的对象,并调起__call__()方法,我们可以通过在__call__()方法内部增加函数,如验证登录、记录日志等,这就是类装饰器。

 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值