Python 函数式编程-高阶函数

文中知识点和代码示例学习自慕课网,python进阶部分(http://www.imooc.com/learn/317) 学习笔记

把函数作为参数

例1:求两个数字绝对值的和(abs(x)+abs(y))

def add(x,y,f):
    return f(x) + f(y)

# add 函数的x,y,f 参数可以是任何值,如果 f 是个函数,则 x,y 两个参数分别带入到 f 函数中求值后,再做为 add 函数的参数传入。
print add(-5,9,abs)

例2:求√x + √y的值

import math

def add(x, y, f):
    return f(x) + f(y)

print add(25, 9, math.sqrt)

map() 函数

它接收一个函数f和一个list,并通过把函数f依次作用在list的每个元素上,得到一个新的list 并返回。

例1:把列表总每个元素都取二次方

def f(x):
    return x * x
print map(f,[1,2,3,4,5,6,7,8,9])

#[1, 4, 9, 16, 25, 36, 49, 64, 81]

例2:将列表中的字符串都变为首字母大写

def format_name(s):
    return s.title()
print map(format_name, ['adam', 'LISA', 'barT'])

#['Adam', 'Lisa', 'Bart']

reduce() 函数

reduce()函数接收的参数和map()类似,一个函数f,一个list,但行为和map()不同,reduce()传入的函数f必须接收两个参数,reduce()list的每个元素反复调用函数f,并返回最终结果值。

例1:求一个列表中所有元素的和

def f(x,y):
    return x + y
print reduce(f,[1,2,3,4,5])

先计算头两个元素:f(1, 2),结果为3;
再把结果和第3个元素计算:f(3, 3),结果为6;
再把结果和第4个元素计算:f(6, 4),结果为10;
再把结果和第5个元素计算:f(10, 5),结果为15;
由于没有更多的元素了,计算结束,返回结果15.

#15

filter() 函数

filter()函数接收一个函数f和一个list,这个函数f的作用是对每个元素进行判断,返回TrueFalse,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list

例:删除一个list中的偶数,保留奇数

def is_odd(x):
    return x % 2 == 1
print filter(is_odd(),[1,2,3,4,5,6,6,7,8,9])

#[1, 3, 5, 7, 9]

可以用来删除一个列表中我们不需要的元素。也可以使用如下方法来完成:

[i for i in [1,2,3,4,5,6,6,7,8,9] if i % 2 ==1 ]

#[1, 3, 5, 7, 9]

sorted() 函数

Python内置的 sorted()函数可对list进行排序:

>>>sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]

但 sorted()也是一个高阶函数,它可以接收一个比较函数来实现自定义排序,比较函数的定义是,传入两个待比较的元素 x, y,如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。

例:实现倒序排列

def reversed_cmp(x, y):
    if x > y:
        return -1
    if x < y:
        return 1
    return 0

print sorted([36, 5, 12, 9, 21], reversed_cmp)

#[36, 21, 12, 9, 5]

其他方法:

# 先排序,后反转
>>> sorted([36, 21, 12, 9, 5])[::-1]
[36, 21, 12, 9, 5]
# 使用reversed()方法,返回一个迭代器。 如果使用reverse(),是直接修改原列表,不会返回新的列表
>>> list(reversed(sorted([36, 5, 12, 9, 21])))
[36, 21, 12, 9, 5]

返回函数

Python的函数不但可以返回intstrlistdict等数据类型,还可以返回函数!

例1:定义一个函数 f(),我们让它返回一个函数 g

def f():
    print 'call f()...'
    # 定义函数g:
    def g():
        print 'call g()...'
    # 返回函数g:
    return g
    
>>> x = f()   # 调用f()
call f()...
>>> x   # 变量x是f()返回的函数g:

>>> x()   # x指向函数g,因此可以调用
call g()...   # 调用x()就是执行g()函数定义的代码

例2:写一个函数calc_prod(lst),它接收一个list,返回一个函数,返回函数可以计算参数的乘积。

def calc_prod(lst):
    def f(x,y):
        return x * y
    def chengji():
        return reduce(f,lst)
    return chengji

f = calc_prod([1, 2, 3, 4])
print f()

闭包

例如上一小节返回函数中的例2chengji内层函数引用了外层函数calc_prodlst变量。像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。

闭包的特点:返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下:

# 希望一次返回3个函数,分别计算1x1,2x2,3x3:
def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()

#9 9 9

原因就是当count()函数返回了3个函数时,这3个函数所引用的变量i的值已经变成了3。由于f1、f2、f3并没有被调用,所以,此时他们并未计算i*i,当 f1 被调用时:

>>> f1()
9     # 因为f1现在才计算i*i,但现在i的值已经变为3

因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。因此改成如下代码:

# 法一
def count():
    fs = []
    for i in range(1, 4):
        def f(j):
        # 借助函数f来避免引用循环变量i
            def g():
                return j*j
            return g
        r = f(i)
        fs.append(r)
    return fs

f1, f2, f3 = count()
print f1(), f2(), f3()

#1 4 9

# 法二
def count():
    fs = []
    def f(j):
        def g():
            return j*j
        return g

    for i in range(1, 4):
        r = f(i)
        fs.append(r)
    return fs

f1, f2, f3 = count()
print f1(), f2(), f3()

#1 4 9

匿名函数 lambda

高阶函数可以接收函数做参数,有些时候,我们不需要显式地定义函数,直接传入匿名函数更方便。

Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算f(x)=x*x时,除了定义一个f(x)的函数外,还可以直接传入匿名函数

>>> a = lambda x:x*x
>>> a(5)
25
>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果。

def is_not_empty(s):
    return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', '  ', 'END'])

返回函数的时候,也可以返回匿名函数:

# 使用 if..else 来实现abs函数的功能
>>> myabs = lambda x: -x if x < 0 else x 
>>> myabs(-1)
1
>>> myabs(1)
1

例:使用匿名函数简化代码

# 源代码
def is_not_empty(s):
    return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', '  ', 'END'])

# 使用匿名函数
print filter(lambda x:x and len(x.strip()) > 0,['test', None, '', 'str', '  ', 'END'])

# 使用 for..in..if 语句
print [i for i in ['test', None, '', 'str', '  ', 'END'] if i and len(i.strip())>0]

偏函数

当一个函数有很多参数时,调用者就需要提供多个参数。如果减少参数个数,就可以简化调用者的负担。

比如,int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换:

>>> int('12345')
12345

int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做 N 进制的转换:

>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565

假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去:

def int2(x, base=2):
    return int(x, base)
    
# 这样,我们转换二进制就非常方便了:
>>> int2('1000000')
64
>>> int2('1010101')
85

functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

所以,functools.partial可以把一个参数多的函数变成一个参数少的新函数,少的参数需要在创建时指定默认值,这样,新函数调用的难度就降低了。

你可能感兴趣的:(Python 函数式编程-高阶函数)