Python 手记(二):装饰器

一、装饰器decorator的作用与组成:

作用:在不修改被修饰函数主体及调用方式的前提下,为被修饰函数提供新的功能
组成:内嵌函数+高阶函数+闭包=》装饰器

内嵌(部)函数:

定义:在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)
例如:

>>>
>>> num=1
>>> def level1():
...     num=2
...     print(num,'level1')
...     def level2():
...         num=3
...         print(num,'level2')
...         def level3():
...             num=4
...             print(num,'level3')
...         level3()
...     level2()
...
>>> level1()
2 level1
3 level2
4 level3
>>>

注释掉level3中的num赋值,则num继承上一级level2作用域中的值:

>>> num=1
>>> def level1():
...     num=2
...     print(num,'level1')
...     def level2():
...         num=3
...         print(num,'level2')
...         def level3():
...             #num=4
...             print(num,'level3')
...         level3()
...     level2()
...
>>> level1()
2 level1
3 level2
3 level3

注释掉level2、level3中的num赋值,则level2、level3中num均继承level1作用域中的值:

>>> num=1
>>> def level1():
...     num=2
...     print(num,'level1')
...     def level2():
...         #num=3
...         print(num,'level2')
...         def level3():
...             #num=4
...             print(num,'level3')
...         level3()
...     level2()
...
>>> level1()
2 level1
2 level2
2 level3
>>>

总结:变量在函数中的作用域只能正向继承,但层级越近优先级越高,本地优先级最高,全局优先级最低。

高阶函数:

满足下列条件之一的函数,即可被称之为高阶函数:
1.某一函数当做参数传入另一个函数中
2.函数的返回值包含n个函数,n>0

例如:

>>> def foo(func):   ####stage2
...     res=func()    #####stage3
...     return res    ####stage4
...
>>> def calc():
...     print([i for i in range(3)])
...     return 'run end'
...
>>>
>>> foo(calc)  ####stage1
[0, 1, 2]
'run end'

执行过程:
stage1.将calc函数主体这个对象,calc作为实参传递给foo,替代形参func
stage2:运行foo函数
stage3:将calc()赋值给变量res,并开始执行calc()函数,
stage4:将clac()的函数返回值传递给res

好像并没有什么X用,绕了一圈不如直接调用calc(),那么接着往下

闭包

python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行了引用,并且函数返回值也是内部函数本身,那么该内部函数就被认为是闭包(closure)。

def outside _func(para1):
  def inside_func(para2):
    return para1*para2
  return inside_func  # 将inside_func作为outside_func的返回值

a = outside_func(12)   # 此时 para1=12,执行outside_func(12)
print(a)             #此时的inside_func并未赋予参数被执行,因此上方执行的结果只能是一段inside_func的内存指针               
print(a(21))    # para2=21,等价于print(outside_func(12)(21))

此时的inside_func就叫做闭包,闭包的作用:
1.隐藏内部函数,只暴露外部函数
2.如果内部函数使用了外部函数的变量,则此外部变量不会随着外部函数一起销毁,而会一直保存在内存中

二、内嵌函数+高阶函数+闭包=》装饰器

假设要给函数增加一个记录运行时间的功能,要求不改变函数主体和调用方式:

**

>>> import time
>>> def timmer(func):      #stage3
...     def warpper():       
...         start_time=time.time()   #stage4
...         func()				#stage5,link to the function test1()
...         stop_time=time.time()   #stage6
...         print('the func run time is %s second' %(stop_time-start_time))
...     return warpper   #stage7
...
>>>
>>> def test1():
...     time.sleep(1)
...     print('in the test1')
...
>>> test1 = timmer(test1)   #stage1
>>> test1()          #stage2
in the test1
the func run time is 1.0000572204589844 second
>>>

过程解析:
stage1:将test1函数作为实参传递给timmer函数,运算得出的结果的内存指针再重新指向给test1
stage2:开始执行test1(读取test1内存指针并开始运算)
stage3:执行timmer(func),即timmer(test1)
stage4:执行warpper,记录当前时间赋值给start_time
stage5:执行func(),即test1()函数
stage6:test1()执行完成后记录当前时间,赋值给stop_time
stage7:warpper的执行结果return给timmer,执行全部完成。

ps:stage1中的test1=timmer(test1)看起来比够简洁,在python中有语法糖这么一个存在,@符号表示,test1=timmer(test)等价于在定义test1的上方@timmer

**

函数在执行过程中可能会带有参数,那么带参数的函数如何配合装饰器中使用呢?改良版:

**

>>> import time
>>> def timmer(func):
...     def warpper(*args,**kwargs):    #可接受任意类型参数
...         start_time=time.time()
...         func(*args,**kwargs)
...         stop_time=time.time()
...         print('the func run time is %s second' %(stop_time-start_time))
...     return warpper
...
>>>@timmer   #等价于test1=timmer(test1)
>>> def test1(n):
...     time.sleep(n)
...     print('in the test1')	
...		return 'run end'
...	
>>> test1(2)   #等价于timmer(test1)(2),test1作为timmer的参数,2作为warpper的参数
in the test1
the func run time is 2.0001142024993896 second
>>>

函数可能会手动定义返回值,例如上方定义了return ‘run end’,但是经过装饰器包装了之后现在并没有返回值,如何保留函数原本的返回值呢?进阶版:

>>> import time
>>> def timmer(func):
...     def warpper(*args,**kwargs):
...         start_time=time.time()
...         func(*args,**kwargs)
...         stop_time=time.time()
...         print('the func run time is %s second' %(stop_time-start_time))
...         return func(*args,**kwargs)
...     return warpper
...
>>> @timmer
... def test1(n):
...     time.sleep(n)
...     return 'run end'
...
>>> test1(1)
the func run time is 1.0000572204589844 second
'run end'
>>>

在闭包函数内最后一行添加一个return func()即可返回func()函数原本的return返回值

若此时要求实现较为复杂的逻辑,装饰器本身也要携带不同的参数以实现多元化的功能。例如在上方例子基础上,要求增加在不同的板块内计时的倍率不同的功能。高级版:

>>> import time
>>> def timmer_rate(rate_type):
...     print('Current rate is %s rate' % rate_type )
...     def timmer(func):
...         def warpper(*args,**kwargs):
...             start_time=time.time()
...             func(*args,**kwargs)
...             stop_time=time.time()
...             run_time=stop_time-start_time
...             if rate_type == 'normal':
...                 print('the func run time is %s second' % run_time)
...             elif rate_type == 'double':
...                 print('the func run time is %s second' %(run_time*2))
...             return func(*args,**kwargs)
...         return warpper
...     return timmer
...
>>>
>>> @timmer_rate('normal')
... def test1(n):
...     time.sleep(n)
...     return 'test1 run end'
...
Current rate is normal rate
>>> @timmer_rate('double')
... def test2(n):
...     time.sleep(n)
...     return 'test2 run end'
...
Current rate is double rate
>>> test1(4)
the func run time is 4.0002288818359375 second
'test1 run end'
>>> test2(2)
the func run time is 4.0002288818359375 second
'test2 run end'
>>>

相比上方版本,只是在原本两层的装饰器外再添加了一层,可以用来接收装饰器本身的参数,在装饰器内层加以判断,以实现差异化的功能,最终在test1内执行4s和test2内执行2s,计时结果时间接近一致。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值