乐趣区

关于python:Python7函数下

引言

这篇文章介绍 python 中函数的基本知识。

文章目录

0×8. 返回函数
0×9. 匿名函数
0×10. 函数装璜器
0×11. 偏函数

0×8. 返回函数

高阶函数除了能够承受函数作为参数外,还能够把函数作为后果值返回,被返回的函数并没有立即执行,直到被调用时才执行其中的内容,请看上面的实例:

#!/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

将函数作为返回值容易呈现一些小问题,当返回函数中的值调用了内部的一些变量的时候,如果内部的变量产生扭转,那么可能失去意料之外的后果,例如:

#!/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 定义,例如:

#!/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 函数实现将字符串转换成浮点数的一个实例:

#!/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

#两局部相加失去最终后果

匿名函数也能够用作返回函数,例如:

#!/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. 函数装璜器

有时候咱们须要在函数运行前执行一些操作,但又不能更改原函数,这个时候就可能用到 ” 函数装璜器 ”,所谓的装璜器,实际上就是一个函数,它接管原函数作为传入参数,而后返回另外一个函数,这个返回的函数在原函数执行前,将执行一系列咱们设定的操作,而后再执行原函数,请看上面的实例:

#!/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 函数参数中

在下面的实例中,装璜器自身只能接管被装璜的函数作为传入参数,如果想要装璜器自身也能接管参数,须要更改程序如下:

#!/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()函数,前面的步骤和上一个实例就一样了

下面的实例中,如果咱们在程序开端增加一个打印调用函数名称的语句:

......
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”,而后依据传入装璜器的参数判断是应用带参数的装璜器还是一般装璜器,请看上面的实例:

#!/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 的函数提供的性能,这个函数接管的第一个参数是一个函数,前面顺次是要传递给这个函数的参数,请看上面的实例:

#!/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,否则会返回传入的最大值
退出移动版