简介本文介绍了python中函数的基础知识。文章目录0×8。返回函数0×9。匿名函数0×10。函数装饰器0×11。偏函数0×8。返回函数除了接受函数作为参数外,高阶函数还可以将函数作为结果值返回,返回的函数不会立即执行,其内容直到被调用才会执行,请看下面的例子:#!/usr/bin/envpython#coding=utf-8#--------defcalc_sum(*n):"""立即计算并返回传入的所有参数之和"""s=0forxinn:s+=xreturns#--------deflazy_sum(*n):"""将n_sum函数返回给调用者,只在函数被访问时计算所有参数的和."""defn_sum():s=0forxinn:s+=xreturnsreturnn_sumf1=calc_sum(1,2,3,2,1)f2=lazy_sum(1,2,3,2,1)#f1接收calc_sum函数的返回结果print(f1)#f2只接收一个返回函数n_sumprint(f2)#访问n_sum函数时计算结果print(f2())#程序输出9.n_sumat0x7fc0410cae18>9使用函数作为返回值容易出现一些小问题,当返回函数的值调用一些外部变量时,如果外部变量发生变化,可能会得到意想不到的结果,例如:#!/usr/bin/envpython#coding=utf-8#--------defcount():L=[]foriinrange(1,4):deffx():returni*iL.append(fx)returnLf1,f2,f3=count()print(f1())print(f2())print(f3())#这个程序count()会生成三个函数[fx,fx,fx]在L列表中执行后,这三个函数给定三个变量f1、f2、f3,期望的函数执行结果应该是1、4、9,但实际上三个函数打印出来的结果都是9,这是因为,当我们调用f1()时,f2(),在f3()之前,不会计算i*i的结果,而当我们调用这三个函数时,count()函数已经执行完毕,for循环后i的值为3,所以f1(),f2(),f3()都是return9#如果希望得到1,4,9这样的结果,应该在for执行的时候把i参数传给return函数,让传入的参数保存起来在那个函数实例中,这样调用的时候就会得到相应的结果,像下面的#!/usr/bin/envpython#coding=utf-8#------defcount():deff(i):deffx():returni*ireturnfxL=[]foriinrange(1,4):L.append(f(i))returnLf1,f2,f3=1count()print()print(f2())print(f3())#L.append(f(i))每次执行都会把当前的i值传递给f(),这个i值保留在当前的f()instance,而f()本身返回一个函数fx(),所以i*i不是立即计算的。count执行完后,L列表中仍然保存着返回的三个fx函数,直到调用时才进行计算。每个fx函数中i的乘积为0×9。匿名函数匿名函数使用关键字lambda定义,例如:#!/usr/bin/envpython#coding=utf-8#------defprint_website():"""返回网站地址"""return"www.qingsword.com"#------defsqu(x):"""求x的平方"""returnx*xa=lambda:"www.qingsword.com"b=lambdax:x*xprint(print_website())print(squ(12))print(a())print(b(11))#程序输出www.qingsword.com144www.qingsword.com121#in上面的小程序中,前两个是普通def定义的函数,后面的a和b是lambda定义的匿名函数,两者的功能完全一样#匿名函数的语法如下,参数list相当于def函数名括号里面的部分,返回值相当于def函数的返回部分:lambdaparameterlist:returnvalue#看一个匿名函数实例,接收两个参数并返回之和这两个参数#!/usr/bin/envpython#coding=utf-8s=lambdax,y:x+yprint(s(12,6))#程序输出18了解了匿名函数的工作原理之后,很多前面的函数可以改成匿名函数使程序更简洁,下面是使用匿名函数配合map和reduce函数将字符串转换为浮点数的例子:#!/usr/bin/envpython#coding=utf-8fromfunctoolsimportreducedefstr2float(s):returnreduce(lambdax,y:x*10+y,map(int,s[:s.find(".")]))+\reduce(lambdax,y:x/10+y,map(int,s[:s.find("."):-1]))/10print(str2float("520.1314"))#输入一个浮点数字符串和输出一个浮点数。我们把这个程序的返回部分分解一下#reduce(lambdax,y:x*10+y,map(int,s[:s.find(".")]))#这部分完成部分的转换字符串小数点前到整数#s[:s.find(".")]切片会取出第一个字符到"."position(本例中为3),即0、1、2三个索引位置的字符(即“520”)#然后用map将这三个字符转换成int形式,保存为可迭代类型#reduce会把上一步map得到的结果集一一读取出来,用一个匿名函数进行处理。本例先取出5和2得到52,再取出0得到520#reduce(lambdax,y:x/10+y,map(int,s[:s.find(".")):-1]))/10#这部分完成了字符串小数点后面部分到浮点数的转换#唯一需要说明的是s[:s.find("."):-1]#这个切片从-1(结束号)的位置开始,倒序读取(末尾的-1表示倒序,如果是-2表示倒序读取间隔为2),直到“.”position#这部分最终会得到0.1314#将两部分相加得到最终结果匿名函数也可以作为返回函数,例如:#!/usr/bin/envpython#coding=utf-8defa(x,y):returnlambda:x+yb=a(1,2)print(b)print(b())#程序输出,x+y的值只有在访问b()时才会计算.在0x7f1c808a5d90>30×10。函数装饰器有时候我们需要在函数运行之前进行一些操作,但是不能改变原来的函数。这时候就可以使用“函数装饰器”了。所谓装饰器,其实就是一个函数,接收原函数作为传入参数,然后返回另一个函数。返回的函数在原函数中执行。之前会执行我们设置的一系列操作,然后执行原来的函数,请看下面的例子:#!/usr/bin/envpython#coding=utf-8importtime#-------#log函数返回一个wrapper函数,直到返回的函数被调用才会执行。返回函数执行完成后,会打印出传递函数的名称(fx.__name__),返回时会调用传递函数deflog(fx):"""Adecoratorthattakesaincomingfunctionandreturns一个函数。"""defwrapper(*args,**kw):print("Execute%s()"%fx.__name__)returnfx(*args,**kw)returnwrapper#-------#在原来的函数之前,使用@符号加一个装饰器函数,相当于在这个位置执行了一句now=log(now),将now函数名变量重定向到日志函数中,并且原始函数现在作为参数传递给日志函数。这条语句执行完后,就相当于log函数返回的now="wrapper()函数",执行程序now()就相当于执行了log中的wrapper。()Function@logdefnow():"""打印当前时间"""print(time.ctime(time.time()))print("End")now()#程序输出执行now()SatSep1009:07:282016End#上面的wrapper函数可以接收任意数量的参数,将程序改为如下#!/usr/bin/envpython#coding=utf-8importtime#--------deflog(fx):"""接收传入函数并返回函数的装饰器"""defwrapper(*args,**kw):print("Execute%s()"%fx.__name__)returnfx(*args,**kw)returnwrapper#------@logdefnow(*x,**kw):"""打印当前时间"""print(time.ctime(time.time()))print(x)now("www.qingsword.com")#programoutputExecutenow()SatSep1009:25:452016('www.qingsword.com',)#Executingnow()相当于执行wrapper(),因为@log相当于now=log(now),log()返回wrapper(),相当于now=wrapper(),wrapper()可以接收任意参数,所以传入参数“www.qingsword.com”会被传递给returnfx(*args,**kw)的fx函数参数,在上面的例子中,装饰器本身只能接收被装饰函数作为传入参数,如果想让装饰器自己接收参数,需要修改程序如下:#!/usr/bin/envpython#coding=utf-8importtime#------deflog(text):"""接收传入参数的装饰器"""defdecorator(fx):"""接受传入函数并返回函数。"""defwrapper(*args,**kw):print("%s%s()"%(text,fx.__name__))返回fx(*args,**kw)返回包装返回decorator#--------#VALUE!defnow(*x,**kw):"""打印当前时间"""print(time.ctime(time.time()))print(x)now("www.qingsword.com")#程序输出Executenow()SatSep1009:33:422016('www.qingsword.com',)#@log("Execute")相当于now=log("Execute")(now),首先调用log传入参数"Execute",log函数会返回装饰器函数,这个函数接收一个传入的参数,在这个例子中,传递给装饰器的now函数,装饰器函数执行完后,返回一个wrapper()函数,后面的步骤和前面的例子一样。在上面的例子中,如果我们在程序末尾添加一条打印调用函数名的语句:......now("www.qingsword.com")print(now.__name__)#Programoutputwrapper#这是因为装饰器把原来的函数now包装成一个wrapper,now变量指向wrapper函数,有时候会带来很多麻烦,那么如何让程序名输出为原来的名字功能,而不是装饰者的名字?更改如下#!/usr/bin/envpython#coding=utf-8importtimeimportfunctools#------deflog(text):"""接收传入参数的装饰器"""defdecorator(fx):"""接收一个传入的函数并返回一个函数"""#在wrapper上添加一个装饰器,将原函数传入。这条语句执行后,返回的wrapper函数中的__name__字段将替换为fx.__name__,即原函数的名字@functools.wraps(fx)defwrapper(*args,**kw):print("%s%s()"%(text,fx.__name__))返回fx(*args,**kw)返回包装返回decorator#--------#VALUE!defnow(*x,**kw):"""打印当前时间"""print(time.ctime(time.time()))print(x)now("www.qingsword.com")print(now.__name__)#ProgramoutputExecutenow()SatSep1009:43:122016('www.qingsword.com',)now最后看一个完整的例子,要求装饰器在原函数执行前输出“begincall”,在原函数执行后输出“endcall”,然后根据传入的参数判断是使用带参数的装饰器还是普通的装饰器到装饰器,请看下面的例子:#!/usr/bin/envpython#coding=utf-8importtimeimportfunctools#------deflog(TxtOrFx):#判断输入是函数还是一个字符串iftype(TxtOrFx)==type(""):defdecorator(fx):@functools.wraps(fx)defwrapper(*args,**kw):print("Begincall")txFx"Ts(%print("%),FX.__Name__))FX(*ARGS,**KW)Print("EndCall")返回包装器返回装饰器Else:@Functools.wrats(TXTORFX)defone(*ARGS,**KW):PRINT("""call")print("执行%s()"%(TxtOrFx.__name__))TxtOrFx(*args,**kw)print("结束调用")returnwrapper#--------#VALUE!defnow(*x,**kw):"""打印当前时间"""print(time.ctime(time.time()))print(x)now("www.qingsword.com")print(now.__name__)#程序输出,如果将@log("Execute")替换为@log,也会得到如下输出结果,但是执行这两个代码块是不同的BegincallExecutenow()SatSep1010:14:582016('www.qingsword.com',)Endcallnow0×11.PartialfunctionPartialfunction是由一个叫做partial的函数提供的函数functools模块,这个函数接收到的第一个参数是一个函数,接下来是传递给这个函数的参数,请看下面的例子:#!/usr/bin/envpython#coding=utf-8importfunctools#-------defdivi(x,y):"""returnx%y"""returnx%y#第一个偏函数,10是传递给divi函数的第一个参数,相当于默认参数divi(x=10,y),此时使用d0只要传入第二个参数y的值即可完成操作d0=functools.partial(divi,10)print(d0(3))#d1定义partial函数自带两个默认参数,调用d1时不需要传入参数,相当于divi(10,6)d1=functools.partial(divi,10,6)print(d1())#将字符串“520”转换为整数520f1=functools.partial(int,"520")print(f1())#int接受一个关键字参数base来定义传递给的字符串的基数诠释。在这个例子中相当于将传递给int的字符串从二进制转换为十进制。如果调用时只传递一个字符串,则默认按二进制处理。也可以手动传入一个基值,比如base=16,传入的字符串会被当作十六进制处理f2=functools.partial(int,base=2)print(f2("1110"))print(f2("1110",base=16))#程序输出14520144368#又如求最大值的偏函数#!/usr/bin/envpython#coding=utf-8importfunctoolsmax2=functools.partial(max,100)print(max2(10,20,30,200))#程序输出200#max函数接受多个传入值,相当于max(*args)。我们在定义max2的时候,预先预设了一个传入的值,相当于max(100,*args),如果调用max2传入的值小于100,则直接返回100,否则传入的最大值在将被退回