Python:闭包函数与装饰器
闭包函数:
定义:内层函数对外层函数非全局变量的引用,就叫做闭包函数。
闭包会一直存在内存当中,不会因为函数执行结束而被释放。
基本模型:
def wrapper(): name = 'alex' def inner(): print(name) inner() wrapper() alex
判断是否为闭包函数:
可以通过print(函数名.__closure__ )看是否有<cell xxxx xxx >类似信息打印,如果有就说明该函数为闭包函数。
name = 'alex' def wrapper(): def inner(): print(name) inner() print(inner.__closure__) # None 说明该函数不是闭包函数 wrapper() None
name = 'alex' def wrapper(x): x = name def inner(): print(x) inner() print(inner.__closure__) # <cell at 0x0000000000130C48: str object at 0x0000000001E8A260> 有类似的打印信息说明该函数为闭包函数 wrapper(name) alex (<cell at 0x02B77490: str object at 0x00FF73A0>,)
应用场景:
#功能获取网页内容 from urllib.request import urlopen def zhua(url): #url='www.baidu.com' def get(): return urlopen(url).read() return get f=zhua('www.baidu.com') f()
总结:一个函数内部如果需要某个名字关系,有两种方法:
1、通过传参的方式
2、通过闭包的方式,在该函数外层包一层作用域(即再定义一个函数),将需要的名字关系包在外部函数中
装饰器:
定义:用来装饰其他函数,装饰器本身可以是任意可调用对象,被装饰的对象也可以是任意可调用对象。装饰器的本质就是闭包
原则:
1、不修改被装饰对象的源代码。
2、不修改被装饰对象的调用方式。
功能:在遵循原则的前提,为被装饰对象添加上新功能。
演变:
下面通过一系列的代码的演变来说明装饰器的作用与为什么要用装饰器:
假设小王在工作中得到一个需求,领导要求他测试以下其他同事写的接口的执行效率。
import time def func1(): time.sleep(0.3) print('非常复杂......') start_time = time.time() func1() end_time = time.time() print('此函数的执行效率%s' %(end_time-start_time))
第一次改版:封装到一个函数中。 #问题是只能测试一个函数。那么开始第二次改版。
import time def func1(): time.sleep(0.3) print('非常复杂......') def timmer(): start_time = time.time() func1() end_time = time.time() print('此函数的执行效率%s' %(end_time-start_time)) timmer()
第二次改版:被测试函数当参数传入,可以测试多个函数的执行效率。 #问题又来了好像改变了原来函数的调用的方式,于是开始了第三次改版。
import time def func1(): time.sleep(0.3) print('非常复杂......') def timmer(f): start_time = time.time() f() end_time = time.time() print('此函数的执行效率%s' %(end_time-start_time)) timmer(func1) #原来是func1()调用的函数,变成了timmer(func1) 进行调用。不好
第三次改版:测试函数执行效率的同时,不要改变原函数的调用方式。 #问题是虽然大体上满足了我的要求,但是增加两行代码,而且多了一个参数。
import time def func1(): time.sleep(0.3) print('非常复杂......') def timmer(f): start_time = time.time() f() end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) f1 = func1 func1 = timmer func1(f1) #好像很接近了,但多了一个参数。不好
第四次改版: 简单的装饰器就诞生了。#新需求每次测试一个函数的执行效率时,都需要加一行 func1 = timmer(func1)代码,麻烦。
import time def func1(): time.sleep(0.3) print('非常复杂......') def func2(): time.sleep(0.3) print('特别复杂......') def timmer(f): def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return inner func1 = timmer(func1) func2 = timmer(func2) func1() func2()
第五次改版:python提出了一个语法糖 @,python解释器如果到@符号就将正下方的函数名作为一个参数传给@符号后面跟着的函数。#新需求如果被测试的函数有参数怎么办呢。
import time def timmer(f): # f = func1 函数名 def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return inner @timmer # @timmer 与 func1 = timmer(func1) 功能相同 def func1(): time.sleep(0.3) print('非常复杂......') func1()
第六次改版:被装饰的函数肯定要有参数的,你现在不能满足,解决这个问题。
def timmer(f): # f = func1 函数名 def inner(*args,**kwargs): # args = (1,2),kwargs {sex:'nv',name:'alex'} #通过给inner设置动态形参数的方式,接收了任意外部传入的实参,并将所有参数以相同的方式传递给了f() ,此时的f()为被测试函数 start_time = time.time() #这样就解决了被测试的函数有参数的问题了。 f(*args,**kwargs) # f(1,2,,sex='nv',name='alex') end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return inner @timmer # func1 = timmer(func1) inner def func1(a,b): time.sleep(0.3) print(a,b) print('非常复杂......') @timmer # func1 = timmer(func1) inner def func2(a,b,name,sex='man'): # f(1,2,,sex='nv',name='alex') time.sleep(0.3) print(a,b,sex,name) print('非常复杂......') func2(1,2) func2(1,2,sex='nv',name='alex') # inner()
第七次改版:被装饰的函数肯定要有返回值的,解决这个问题。
def timmer(f): # f = func2 函数名 def inner(*args,**kwargs): # args = (1,2),kwargs {sex:'nv',name:'alex'} start_time = time.time() ret = f(*args,**kwargs) # f(1,2,,sex='nv',name='alex') #应为现在的f()== 被装饰的函数,通过ret接收到的就是被装饰函数的返回值 end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return ret #通过return ret 将返回值,返回给了函数的执行者,这的执行者其实是timmer()函数 return inner
@timmer # func1 = timmer(func1) inner def func2(a,b,name,sex='man'): # f(1,2,,sex='nv',name='alex') time.sleep(0.3) print(a,b,sex,name) print('非常复杂......') return 666 print(func2(1,2,sex='nv',name='alex')) # inner()
1 2 nv alex
非常复杂......
此函数的执行效率0.30100131034851074
666
通过上面一系列的代码演变,简单的简述了为什么要使用装饰器以及解决的问题。
下面说以下装饰器的基本模型:
def wrapper(f): def inner(*args,**kwargs): """被装饰函数执行之前的操作""" ret = f(*args,**kwargs) """被装饰函数执行之后的操作""" return ret return inner