引言
这篇文章介绍python中函数的基本知识。
文章目录
0×8.返回函数
0×9.匿名函数
0×10.函数装璜器
0×11.偏函数
0×8.返回函数
高阶函数除了能够承受函数作为参数外,还能够把函数作为后果值返回,被返回的函数并没有立即执行,直到被调用时才执行其中的内容,请看上面的实例:
<code class="bash">#!/usr/bin/env python #coding=utf-8 #-------- def calc_sum(*n): """立刻计算并返回传入的所有参数之和""" s=0 for x in n: s+=x return s #-------- def lazy_sum(*n): """将n_sum函数返回给调用者,只有当拜访函数时才计算所有参数之和""" def n_sum(): s=0 for x in n: s+=x return s return n_sum f1=calc_sum(1,2,3,2,1) f2=lazy_sum(1,2,3,2,1) #f1接管的是calc_sum函数的返回后果 print(f1) #f2接管的只是一个返回函数n_sum print(f2) #拜访n_sum函数时才计算结果 print(f2()) #程序输入 9 <function lazy_sum.<locals>.n_sum at 0x7fc0410cae18> 9
将函数作为返回值容易呈现一些小问题,当返回函数中的值调用了内部的一些变量的时候,如果内部的变量产生扭转,那么可能失去意料之外的后果,例如:
<code class="bash">#!/usr/bin/env python #coding=utf-8 #-------- def count(): L = [] for i in range(1, 4): def fx(): return i*i L.append(fx) return L f1, f2, f3 = count() print(f1()) print(f2()) print(f3()) #这段程序count()执行后会在L列表中生成三个函数[fx,fx,fx],将这三个函数赋予三个变量f1,f2,f3,咱们冀望的函数执行后果应该是1,4,9,但实际上print输入三个函数的后果都是9,这是因为,在咱们调用f1(),f2(),f3()之前,并不会去计算i*i的后果,而在咱们调用这三个函数时,count()函数早已执行实现,for循环后i的值是3,所以f1(),f2(),f3()全副返回9 #如果冀望失去1,4,9这样的后果,就应该将i参数在for执行的时候传递给返回函数,让传递过来的参数保留在那个函数实例中,这样调用他们时才会失去对应的后果,就像上面这样 #!/usr/bin/env python #coding=utf-8 #-------- def count(): def f(i): def fx(): return i*i return fx L = [] for i in range(1, 4): L.append(f(i)) return L f1, f2, f3 = count() print(f1()) print(f2()) print(f3()) #L.append(f(i))每次执行时,都会向f()传递以后的i值,这个i值被保留在以后f()实例中,而f()自身返回一个函数fx(),所以i*i并没有立即计算,count执行实现后L列表中依然保留的是三个返回的fx函数,直到他们被调用时,才别离计算每个fx函数中i的乘积
0×9.匿名函数
匿名函数应用关键字lambda定义,例如:
<code class="bash">#!/usr/bin/env python #coding=utf-8 #-------- def print_website(): """返回网站地址""" return "www.qingsword.com" #-------- def squ(x): """求x的平方""" return x*x a=lambda:"www.qingsword.com" b=lambda x:x*x print(print_website()) print(squ(12)) print(a()) print(b(11)) #程序输入 www.qingsword.com 144 www.qingsword.com 121 #在下面这个小程序中,后面两个是失常def定义的函数,上面的a和b是应用lambda定义的匿名函数,两者性能完全相同 #匿名函数的语法如下,参数列表就相当于def函数名称括号外面的局部,返回值就等同于def函数的return局部: lambda 参数列表:返回值 #再来看个匿名函数实例,接管两个参数返回这两个参数的和 #!/usr/bin/env python #coding=utf-8 s=lambda x,y:x+y print(s(12,6)) #程序输入 18
了解了匿名函数的工作原理后,后面很多的函数都能够改成匿名函数的模式让程序更加简洁,上面是应用匿名函数配合map和reduce函数实现将字符串转换成浮点数的一个实例:
<code class="bash">#!/usr/bin/env python #coding=utf-8 from functools import reduce def str2float(s): return reduce(lambda x,y:x*10+y,map(int,s[:s.find(".")]))+\ reduce(lambda x,y:x/10+y,map(int,s[:s.find("."):-1]))/10 print(str2float("520.1314")) #输出一个字符串浮点数,将输入一个数值浮点数,上面拆分一下这个程序的return局部 #reduce(lambda x,y:x*10+y,map(int,s[:s.find(".")])) #这一部分实现字符串小数点前的局部到整数的转换 #s[:s.find(".")]切片将取出第一个字符到"."地位(本例是3),也就是0,1,2这三个索引地位的字符(即"520") #而后应用map将这三个字符转换成int模式,并且保留成可迭代类型 #reduce将一一读取上一步中map失去的那个后果集,并应用匿名函数进行解决,本例为首先取出5和2失去52,再取出0失去520 #reduce(lambda x,y:x/10+y,map(int,s[:s.find("."):-1]))/10 #这一部分实现字符串小数点前面的一部分到浮点数的转换 #惟一须要解释一下的就是s[:s.find("."):-1] #这个切片从-1的地位(开端数字)开始,倒序读取(开端的-1代表倒序,如果是-2代表倒序并且读取距离为2),直到"."地位 #这一部分最初会失去0.1314 #两局部相加失去最终后果
匿名函数也能够用作返回函数,例如:
<code class="bash">#!/usr/bin/env python #coding=utf-8 def a(x,y): return lambda :x+y b=a(1,2) print(b) print(b()) #程序输入,只有当拜访b()时才计算x+y的值 <function a.<locals>.<lambda> at 0x7f1c808a5d90> 3
0×10.函数装璜器
有时候咱们须要在函数运行前执行一些操作,但又不能更改原函数,这个时候就可能用到”函数装璜器”,所谓的装璜器,实际上就是一个函数,它接管原函数作为传入参数,而后返回另外一个函数,这个返回的函数在原函数执行前,将执行一系列咱们设定的操作,而后再执行原函数,请看上面的实例:
<code class="bash">#!/usr/bin/env python #coding=utf-8 import time #-------- #log函数返回一个wrapper函数,返回函数被调用前不会被执行,返回函数被执行后会打印出传入函数的名称(fx.__name__),并且在返回时调用传入函数 def log(fx): """接管一个传入函数并返回一个函数的装璜器""" def wrapper(*args,**kw): print("Execute %s()"%fx.__name__) return fx(*args,**kw) return wrapper #-------- #在原函数前,应用@符号增加装璜器函数,这相当于在这个地位执行了一句now=log(now),now函数名称变量被从新指向了log函数,原函数now被当成参数传递给了log函数,这段语句执行后相当于now="log函数返回的wrapper()函数",程序执行now()就等价与执行log中的wrapper()函数 @log def now(): """打印以后工夫""" print(time.ctime(time.time())) print("End") now() #程序输入 Execute now() Sat Sep 10 09:07:28 2016 End #下面的wrapper函数是可能接管任意数量的参数的,将程序更改成上面的样子 #!/usr/bin/env python #coding=utf-8 import time #-------- def log(fx): """接管一个传入函数并返回一个函数的装璜器""" def wrapper(*args,**kw): print("Execute %s()"%fx.__name__) return fx(*args,**kw) return wrapper #-------- @log def now(*x,**kw): """打印以后工夫""" print(time.ctime(time.time())) print(x) now("www.qingsword.com") #程序输入 Execute now() Sat Sep 10 09:25:45 2016 ('www.qingsword.com',) #执行now()就等同于执行wrapper(),因为@log等价与now=log(now),而log()返回wrapper(),就相当于now=wrapper(),wrapper()可能接管任意参数,所以传入参数"www.qingsword.com"将被传递到return fx(*args,**kw)的fx函数参数中
在下面的实例中,装璜器自身只能接管被装璜的函数作为传入参数,如果想要装璜器自身也能接管参数,须要更改程序如下:
<code class="bash">#!/usr/bin/env python #coding=utf-8 import time #-------- def log(text): """接管传入参数的装璜器""" def decorator(fx): """接管一个传入函数并返回一个函数""" def wrapper(*args,**kw): print("%s %s()"%(text,fx.__name__)) return fx(*args,**kw) return wrapper return decorator #-------- #VALUE! def now(*x,**kw): """打印以后工夫""" print(time.ctime(time.time())) print(x) now("www.qingsword.com") #程序输入 Execute now() Sat Sep 10 09:33:42 2016 ('www.qingsword.com',) #@log("Execute")等同于now=log("Execute")(now),首先调用log传入参数"Execute",log函数会返回decorator函数,这个函数接管一个传入参数,本例将被装璜的now函数传递给了它,decorator函数执行后再返回一个wrapper()函数,前面的步骤和上一个实例就一样了
下面的实例中,如果咱们在程序开端增加一个打印调用函数名称的语句:
<code class="bash">...... now("www.qingsword.com") print(now.__name__) #程序输入 wrapper #这是因为装璜器将原函数now包装成了wrapper,now变量指向的其实是wrapper函数,这有时会带来不小的麻烦,那么如何让程序名称输入为原函数的名称,而不是装璜器的名称呢?更改如下 #!/usr/bin/env python #coding=utf-8 import time import functools #-------- def log(text): """接管传入参数的装璜器""" def decorator(fx): """接管一个传入函数并返回一个函数""" #在wrapper下面也增加一个装璜器,传入原函数,这条语句执行后,会将返回的wrapper函数中的__name__字段替换成fx.__name__,也就是原函数的名称 @functools.wraps(fx) def wrapper(*args,**kw): print("%s %s()"%(text,fx.__name__)) return fx(*args,**kw) return wrapper return decorator #-------- #VALUE! def now(*x,**kw): """打印以后工夫""" print(time.ctime(time.time())) print(x) now("www.qingsword.com") print(now.__name__) #程序输入 Execute now() Sat Sep 10 09:43:12 2016 ('www.qingsword.com',) now
最初来看一个残缺的实例,要求装璜器在原函数执行前输入”begin call”,在原函数执行后输入”end call”,而后依据传入装璜器的参数判断是应用带参数的装璜器还是一般装璜器,请看上面的实例:
<code class="bash">#!/usr/bin/env python #coding=utf-8 import time import functools #-------- def log(TxtOrFx): #判断传入的是函数还是字符串 if type(TxtOrFx)==type(""): def decorator(fx): @functools.wraps(fx) def wrapper(*args,**kw): print("Begin call") print("%s %s()"%(TxtOrFx,fx.__name__)) fx(*args,**kw) print("End call") return wrapper return decorator else: @functools.wraps(TxtOrFx) def wrapper(*args,**kw): print("Begin call") print("Execute %s()"%(TxtOrFx.__name__)) TxtOrFx(*args,**kw) print("End call") return wrapper #-------- #VALUE! def now(*x,**kw): """打印以后工夫""" print(time.ctime(time.time())) print(x) now("www.qingsword.com") print(now.__name__) #程序输入,如果将@log("Execute")替换成@log,同样将失去上面的输入后果,但两者所执行的代码块不一样 Begin call Execute now() Sat Sep 10 10:14:58 2016 ('www.qingsword.com',) End call now
0×11.偏函数
偏函数是functools模块的一个叫做partial的函数提供的性能,这个函数接管的第一个参数是一个函数,前面顺次是要传递给这个函数的参数,请看上面的实例:
<code class="bash">#!/usr/bin/env python #coding=utf-8 import functools #-------- def divi(x,y): """返回x%y""" return x%y #第一个偏函数,10作为传递给divi函数的第一个参数,相当于默认参数divi(x=10,y),这个时候应用d0只有传递给他第二个参数y的值,就能实现运算 d0=functools.partial(divi,10) print(d0(3)) #d1定义的偏函数自带了两个默认参数,调用 d1的时候就不须要传入参数了,相当于divi(10,6) d1=functools.partial(divi,10,6) print(d1()) #将字符串"520"转换成整数520 f1=functools.partial(int,"520") print(f1()) #int承受一个关键字参数base来定义传入给int的字符串是多少进制,本例等同于将传入给int的字符串当成二进制转换成十进制,调用的时候如果只专递一个字符串,默认当做二进制解决,也能够手动传入一个base值,比方base=16,传入的字符串会当做16进制解决 f2=functools.partial(int,base=2) print(f2("1110")) print(f2("1110",base=16)) #程序输入 1 4 520 14 4368 #再例如求最大值的偏函数 #!/usr/bin/env python #coding=utf-8 import functools max2=functools.partial(max,100) print(max2(10,20,30,200)) #程序输入 200 #max函数承受多个传入值,相当于max(*args),咱们在定义max2的时候,提前预设了一个传入值,这相当于max(100,*args),如果调用max2传入的值比100小,那么间接会返回100,否则会返回传入的最大值