Python5 函数式编程(Functional Programming)

高阶函数Higher-order function

变量可以指向函数

以abs()函数举例,把函数本身赋值给变量:

>>> f = abs
>>> f
<built-in function abs>

结论:函数本身也可以赋值给变量,即:变量可以指向函数。如果一个变量指向了一个函数,那么,可通过该变量来调用这个函数。直接调用abs()函数和调用变量f()完全相同。

函数名也是变量

对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数

注:由于abs函数实际上是定义在import builtins模块中的,所以要让修改abs变量的指向在其它模块也生效,要用import builtins; builtins.abs = ?

传入函数

既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。编写高阶函数,就是让函数的参数能够接收别的函数。


map/reduce

Python内建了map()reduce()函数。

map()函数接收两个参数,一个是函数,一个是Iterablemap将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

reduce()函数的用法是把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce结果继续和序列的下一个元素做累积计算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

举例

利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']

from functools import reduce

IN = ['adam','LISA','barT']

def standarize(IN):
    return IN[0].upper() + IN[1:].lower()
print(list(map(standarize,IN)))

Python提供的sum()函数可以接受一个list并求和,请编写一个prod()函数,可以接受一个list并利用reduce()求积:

def prod(x,y):
    return x*y

print(reduce(prod,IN))

利用mapreduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456

from functools import reduce

IN = '123.456'
decimal = 0
def str2float(x,y):
    global decimal
    if y == '.':
        return x+0.0

    if isinstance(x,int):
        return x*10+y
  else:   decimal
= decimal + 1   return x+y/10**decimaldef char2num(L): return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,'.':'.'}[L] print(reduce(str2float,map(char2num,IN)))

filter

Python内建的filter()函数用于过滤序列

map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值True还是False决定保留还是丢弃该元素

把一个序列中的空字符串删掉,可以这么写:

def not_empty(s):
    return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
# 结果: ['A', 'B', 'C']

可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数。

注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。

用filter求素数

def main():
    for n in primes():
        if n < 100:
            print(n)
        else:
            break

def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n

def _not_divisible(n):
    return lambda x: x % n > 0  #余数

def primes():
    yield 2
    it = _odd_iter()
    while True:
        n = next(it)
        yield n
        it = filter(_not_divisible(n), it)

sorted

排序算法

排序的核心是比较两个元素的大小。Python内置的sorted()函数就可以对list进行排序。此外,sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]

我们再看一个字符串排序的例子,实现忽略大小写的排序:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']

要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True。

一组tuple排序

L=[('Bob',75),('Adam',92),('Bart',66),('Lisa',88)]
def by_name(t):
    return t[0]
L2=sorted(L,key=by_name)
print(L2)


返回函数

函数作为返回值

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

我们来实现一个可变参数的求和

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:

>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>

调用函数f时,才真正计算求和的结果:

>>> f()
25

在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”。

闭包

注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。

另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。

返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

如果一定要引用循环变量怎,方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

def count():
    def f(j):
        return lambda : j*j

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

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

result:
<class,'function'>
1 4 9

匿名函数

当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。

Python对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

通过对比可以看出,匿名函数lambda x: x * x关键字lambda表示匿名函数,冒号前面的x表示函数参数

匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数,同样,也可以把匿名函数作为返回值返回:

>>> f = lambda x: x * x
>>> f
<function <lambda> at 0x101c6ef28>
>>> f(5)
25

装饰器

函数对象有一个__name__属性,可以拿到函数的名字

>>> def now():
...     print('2015-3-25')
>>> f = now
>>> now.__name__
'now'
>>> f.__name__
'now'

现在,假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。

和两层嵌套的decorator相比,3层嵌套的效果是这样的:

>>> now = log(now)  #两层

>>> now = log('execute')(now) #三层

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。

写一个decorator,能在函数调用的前后打印出'begin call''end call'的日志。

import functools
def log(arg=None):
    if isinstance(arg, str):
        print(arg)
        return log

    @functools.wraps(arg)
    def decorator():
        print('Start')
        arg()
        print('End')
    return decorator

@log
def now():
    print('Processing')

if __name__ == '__main__':
    now()

偏函数

functools.partial就是帮助我们创建一个偏函数的,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数。

注意到上面的新的int2函数,仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值:

import functools
#偏函数就是设置了默认参数之后的函数
int2 = functools.partial(int, base = 2)
print(int2('10010'))

#如果不指明将那个参数设置成默认值的话,那么python将按自动匹配
max10 = functools.partial(max, 10,9,8)
print(max10(1,2,3,4,5))

创建偏函数时,实际上可以接收函数对象*args**kw这3个参数。

 

整理自:http://www.liaoxuefeng.com

 

posted @ 2017-03-07 17:21  古古卷  阅读(368)  评论(0编辑  收藏  举报

欢迎访问我的博客!