Day11 Python基础之装饰器(高级函数)(九)
在python中,装饰器、生成器和迭代器是特别重要的高级函数 https://www.cnblogs.com/yuanchenqi/articles/5830025.html
装饰器
1.如果说装饰器是取经路上的大妖怪,想要干掉它就必须拿到三样法宝
法宝一:作用域(LEGB)
法宝二:高阶函数:(把函数名看作变量,可以作为传输参数,也可以作为返回值)
(在python的世界里,函数和我们之前的[1,2,3],'abc',8等一样都是对象,
而且函数是最高级的对象(对象是类的实例化,可以调用相应的方法,函数是包含变量对象的对象,牛逼!)。)
法宝三:闭包 (LEGB规则的特例,闭包实现了enclosing变量在enclosing范围之外的使用)
闭包(closure)是函数式编程的重要的语法结构。
定义:如果在一个内部函数里(local),对在外部作用域(但不是在全局作用域)的变量(enclosing)进行引用,
那么内部函数就被认为是闭包(closure).
闭包=函数块+定义函数时的环境,inner就是函数块,x就是环境,当然这个环境可以有很多,不止一个简单的x
def f_outer(): x=10 #enclosing变量(外部环境) def f_inner(): #内部函数(local) print (x) return f_inner #enclosing变量 f_outer()() 输出结果:10
2.装饰器(从实现功能出发理解)
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
概括的讲,装饰器的作用就是在不修改源代码的情况下为已经存在的对象添加额外的功能。
1.重新定义一个函数,为原函数添加功能
2.需要外加一个装饰器(函数)用来传递原函数,然后返回step1中定义的函数
3.如果还需要传其它参数,可以再外加一个装饰器传递参数
简单装饰器的实现
if foo()==show_time(foo) :问题解决!
所以,我们需要show_time(foo)返回一个函数对象,而这个函数对象内则是核心业务函数:执行func()与装饰函数时间计算,修改如下:
函数show_time就是装饰器,它把真正的业务方法func包裹在函数里面,看起来像foo被上下时间函数装饰了。在这个例子中,函数进入和退出时 ,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。
1 import time 2 def foo(): 3 print('Function1') 4 time.sleep(5) 5 6 def bar(): 7 print('Function2') 8 time.sleep(5) 9 10 def show_time(func): 11 def inner(): 12 start=time.time() 13 func() 14 end=time.time() 15 print(end-start) 16 return inner 17 foo=show_time(foo) 18 bar=show_time(bar) 19 20 21 foo() 22 bar()
@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作
1 import time 2 def show_time(func): 3 def inner(): 4 start=time.time() 5 func() 6 end=time.time() 7 print(end-start) 8 return inner 9 10 11 @show_time 12 def foo(): 13 print('Function1') 14 time.sleep(5) 15 16 @show_time 17 def bar(): 18 print('Function2') 19 time.sleep(5) 20 21 # foo=show_time(foo) 22 # bar=show_time(bar) 23 24 foo() 25 bar() 26 输出结果: 27 Function1 28 5.000344753265381 29 Function2 30 5.00098443031311
如上所示,这样我们就可以省去bar = show_time(bar)这一句了,直接调用bar()即可得到想要的结果。如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。
这里需要注意的问题: foo=show_time(foo)其实是把wrapper引用的对象引用给了foo,而wrapper里的变量func之所以可以用,就是因为wrapper是一个闭包函数。
(从宏观的角度理解)
带参数的被装饰函数
1 import time 2 def show_time(func): 3 def inner(x,y): 4 start=time.time() 5 func(x,y) 6 end=time.time() 7 print(end-start) 8 return inner 9 @show_time 10 def add(a,b): 11 print(a+b) 12 time.sleep(5) 13 14 add(2,3) 15 输出结果: 16 5
1 import time 2 def show_time(func): 3 def inner(*args,**kwargs): 4 start=time.time() 5 func(*args) 6 end=time.time() 7 print(end-start) 8 return inner 9 @show_time 10 def add(*args,**kwargs): 11 sum=0 12 for i in args: 13 sum+=i 14 print(sum) 15 time.sleep(5) 16 17 add(2,3,4,5,6) 18 19 输出结果: 20 20
带参数的装饰器
装饰器还有更大的灵活性,例如带参数的装饰器:
在上面的装饰器调用中,比如@show_time,该装饰器唯一的参数就是执行业务的函数。
装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)。
1 import time 2 def time_logger(flag=0): 3 def show_time(func): 4 def inner(*args,**kwargs): 5 start=time.time() 6 func(*args,**kwargs) 7 end=time.time() 8 print(end-start) 9 if flag: 10 print('打印日志') 11 return inner 12 return show_time 13 @time_logger(3) 14 def add(*args,**kwargs): 15 sum=0 16 for i in args: 17 sum+=i 18 print(sum) 19 time.sleep(5) 20 21 add(2,3,4,5,6) 22 23 输出结果: 24 20 25 5.000020742416382 26 打印日志
(说明可以通过装饰器给闭包传输参数)
3.装饰器的应用举例之登录