装饰器:
在装饰器之前是三个比较重要的概念:
1 作用域:L_E_G_B
2 . 高阶函数:
1 函数名可以作为参数输入
2 函数名可以作为返回值
3 闭包
对于作用域之前就说过了,也就是局部变量,嵌套变量,全局变量,系统变量。高阶函数也有介绍,重点在闭包。
什么是闭包呢?这里有个例子:
# 闭包(closure) # 闭包=函数块 + 定义函数时的环境 def outer(): x = 10 def inner(): # inner是个闭包 print(x) # x是外部函数变量的引用 return inner # 只能通过return inner使得能够调用 # outer()() # 调用inner函数 # 不能直接调用inner函数 作用域的问题 inner是局部变量 f = outer() # 拿到了return的inner 本来是拿不到x的值 # 对于它来说outer已经关闭了,但是还是拿到了x,这个就是闭包的现象 f() # f()相当于调用inner函数。 # 闭包的条件: 1 inner是个内部函数 2 外部环境的一个变量 # 对这个变量的引用就是闭包,inner就是闭包函数 # 用处:脱离了outer函数,而能够直接用outer的变量
那么闭包的作用呢,可以说,闭包更好的封装好了一个内部函数,让我们使用这个函数能够更好的拓展的同时又达到一个能够保护内部函数的作用。在装饰器中,这个就是用到的思想。
什么是装饰器呢?这里我们说个例子,假如有多个已经编译好的函数。这时要求增加一个使得能够计算函数运行时间的模块。那该怎么做呢?首先我们想到的一定是在函数中都添加这个模块。但是这样就造成了代码的重复,为了使代码不重复。我们单独将这个模块写成一个函数,同时将其他函数名作为参数传入,这样看似很不错,但是这样我们就改变了函数使用的方式,也就是会将函数的名字改变为此就有了装饰器。首先我们看下是怎么实现的:
# 装饰器 decorator import time start = time.time() print(start) # 1520818272.3146646 指从1970开始的秒数 def show_time(f): def inner(): start = time.time() f() end = time.time() print("spenf: %s"%(end-start)) return inner @show_time #foo = show_time(foo) def foo(): print("foo......") time.sleep(1) end = time.time() print("spenf: %s"%(end-start)) # 1.000467300415039 # 开放封闭原则 :不应该修改封闭好的原函数。而是在其他地方实现扩展 def bar(): print("bar .........") time.sleep(3) # 在其他为了都实现计算在它们 之间加入运行时间的。可以写个函数用于展示使用时间 # def show_time(f): # 为了使所有的函数都有显示运行时间,在参数中添加其他函数的形参 # start = time.time() # f() # end = time.time() # print("spenf: %s"%(end-start)) # show_time(bar) # 但是这样会将调用函数的方式改变,所以是不可以的。 # 改变方式:这个就是装饰器,主要是将show_time写成装饰器函数 # 还可以用@show_time python中的方便方法实现 foo() bar = show_time(bar) bar()
这里很明显我们用到了闭包,也就是将外部函数用于传入参数,同时将内部函数返回,这个时候内部函数就会装载在内存上,同时我们又可以 调用外部函数的变量和环境,再加上我们之前的函数实现的方法,这样我们再重新将调用这个方法的返回的函数重新定义为这个名字。我们就可以实现了,函数名不改变的同时又实现了,这个模块的添加。这就是装饰器的作用。
在这之后,我们又想到一种可能,函数中如果带有参数该怎么办呢?这里又用到昨天的博客中的知识就实现了,不是很复杂直接贴代码:
import time def logger(flag=''): def show_time(f): def inner(*x, **y): start = time.time() f(*x, **y) end = time.time() print("spenf: %s"%(end-start)) if flag == 'true': print("日志记录") return inner return show_time @logger('true') # @show_time def add(*a, **b): sums = 0 for i in a: sums+=i print(sums) time.sleep(2) @logger() def bar(): print("bar .........") time.sleep(3) add(1, 3, 4, 5)
内部函数可以带参数,那么对于被装饰的函数该怎么去传参数呢?比如如果对于被装饰的函数中,我们有的时候想让它输出一个日志文件有时又不想,该怎么办呢?对于这个,我们可以将装饰器再进行一层闭包,这样就可以传入这个参数了,注意外层是不能再传参数的,因为这个是对应了我们用于函数的传参。
import time def show_time(f): def inner(*x, **y): start = time.time() f(*x, **y) end = time.time() print("spenf: %s"%(end-start)) return inner @show_time # add = show_time(add) def add(*a, **b): sums = 0 for i in a: sums+=i print(sums) time.sleep(2) add(1, 3, 4, 5) # 但是参数可能不是定长的,为了改变里面的