当前位置: 首页 > 后端技术 > Python

函数闭包简介

时间:2023-03-25 21:43:09 Python

函数闭包简介什么是闭包?闭包是一个特殊的函数。一个闭包一般由两个函数组成,即一个内层函数和一个外层函数。参数,外层函数返回的结果是内层函数本身。例如:deffunc_out(a):#Externalfunctionb=10#a,b是外部函数的变量deffunc_in(c):#c是内部函数的变量returna+b+c#Theinternalfunction调用外部函数的变量returnfunc_in#返回内部函数本身>>>m=func_out(1)#此时外部函数传入a=1,m本质上就是func_in函数对象>>>m(3)#调用func_in()函数,传递给func_in函数的参数c=314就是闭包的条件。内部函数是在外部函数中定义的。内部函数调用外部函数的参数。外部函数返回内部函数本身。可以看到闭包的一个很大的特点就是内层函数在调用外层函数的临时变量。在python中,一个函数内部的变量如果没有特别声明为全局变量,它们都是函数局部区域下的临时变量。当函数执行时,内部的局部变量也会被回收释放。上面的例子中,a和b还没有被释放,可以调用,因为外层函数还没有执行,内层函数也属于外层函数。可以这样理解:全局变量可以在函数内部直接调用,比如:a=10#externalglobalvariabledeffunc(b):returna+b>>>11因此,在闭包中,如果外层函数的域是A,内层函数的域是B,那么B属于A,或者A是B的相对全局域,那么内层函数自然可以调用外层函数的变量。几点理解闭包返回一个函数对象deffunc_out(a):b=10deffunc_in():returna+breturnfunc_in>>>m=func_out(1)>>>type(m)>>>m(1)12闭包返回内部函数。可以看到调用闭包时,调用结束时会实现内层函数,外层函数更多的是局部变量或者保存初始化其他相关信息。之后,每次调用闭包或内部函数时,都不需要传入这些公共变量。这与具有单个函数的类非常相似,例如:classA:def__init__(self,a,b):self.a=aself.b=bdeffunc(self,c):returnself.a+自我。b>>>m=A(1,10)>>>m.func(1)11上面例子中的类实现的函数和前面的闭包例子是一样的。因此,很多单一方法的类都可以转化为闭包来实现。闭包的典型应用闭包的一个重要应用是作为装饰器。比如我们要对一些需要计算的函数进行计时,对一些函数的入参进行强制类型检查。这种场景可以通过装饰器实现的非常漂亮,而不是在每个函数下重复复制相似的函数。代码。timerdeftimer_func(func):'''调用func函数时用来计时:paramfunc:函数对象作为外层函数的入参:return:innerfunction'''#innerfunctiondefwrapper(*args,**kwargs):#通过*args,**kwargs传入函数的所有参数t1=time.time()r=func(*args,**kwargs)#调用外层函数的内层函数的参数,参数是一个函数对象t2=time.time()cost=t2-t1print('timecost%s'%cost)returnr#returnedfunc(*args,**kwargs)returnwrapperdeffunc(n):whilen>0:n=n-1returnn>>>a=timer_func(func)#返回的是内部包装函数对象>>>a(100000)##10000将*args传入func。timecost0.0099997520446777340可以看出整个装饰函数只是增加了一个定时函数,需要定时的函数怎么调用。所以内部函数一般返回func(args,*kwargs),也就是被修饰函数的执行结果。这个修饰可以直接通过语法糖实现@:@timer_funcdeffunc(n):whilen>0:n=n-1returnn>>>func(9999999)timecost0.7122969627380371typechecking在写一些函数的时候,我们有时你想要指定输入参数的类型。在python中,没有像c语言那样的类型检查等功能,因为python是动态类型语言。一种方式是通过注解来“指定”类型,比如:deffunc(a:int,b:str)->list:res=[a**2,b]returnres#这里的限制只有一个Specifications而reminders不会报错,而是会在IDE中提示。>>>print(func(1,'2'))[1,'2']>>>print(func(2,3))#不会报错[4,3]如果你想输入输入参数对于强制检查,可以使用装饰器。否则,每个函数通过ifelse判断类型。代码会很冗长,不容易突出核心代码。会涉及到inspect库中签名相关的操作:importinspectdeffunc(a:int,b:str)->list:res=[a**2,b]returnres>>>sig=inspect.signature(func)#提取函数的签名并返回一个Signature对象>>>siglist>>>>parameters=sig.parameters#获取参数的有序字典>>>parametersmappingproxy(OrderedDict([('a',),('b',)]))>>>arg_names=tuple(parameters.keys())#GetAllparameternames>>>arg_names('a','b')因此,如果通过构造装饰器对函数的参数进行类型检查,函数的每个参数和期望的类型都会自动提取到里面装饰器,然后循环检查就可以了:defpara_check(func):#外部函数,传入的参数是要检查的函数对象本身sig=inspect.signature(func)#获取函数参数签名参数=sig.parameters#Gettheordereddictionaryofparametersarg_names=tuple(parameters.keys())#获取参数的名称defwrapper(*args,**kwargs):#Innerfunctioncheck_list=[]#待测试的参数对i,valinenumerate(args):#检查所有位置参数arg_name=arg_names[i]anno=parameters[arg_name].annotation#这个参数的预期类型check_list.append((arg_name,anno,val))forarg_name,valinkwargs.items():#检验所有的参数检验anno=parameters[arg_name].annotationcheck_list.append((arg_name,anno,val))forcheck_argincheck_list:#检验所有的参数检验ifnotisinstance(check_arg[2],check_arg[1]):raiseTypeError('输入%s期望类型%s,但得到%s'%(check_arg[0],check_arg[1],type(check_arg[2])))returnfunc(*args,**kwargs)returnwrapper@para_checkdeftest(x:int,y:int):returnx+y>>>print(test(1,2))3>>>print(test(1,'3'))TypeError:输入y期望类型,但得到了