Python支持一些函数式编程的特性。比如lambda、 map()、reduce()、filter()函数。
一:匿名函数与lambda
Python可以用lambda 关键字创造匿名函数。匿名函数不需要以标准的方式来声明(使用def 语句)。作为函数,它们也能有参数。
一个完整的lambda“语句”代表了一个表达式,这个表达式的定义体必须和声明放在同一行。匿名函数的语法如下:
lambda [arg1[, arg2, ... argN]]: expression
参数是可选的,lambda 生成一个可以像其他函数一样使用的函数对象。
比如下面的简单函数:
def true(): return True
对于该函数,可以使用lambda表达式生成等价的函数对象:
>>> lambda :True <function <lambda> at f09ba0>
在上面的例子中,只是简单地用lambda创建了一个函数(对象),但是该函数对象创建完之后就被垃圾回收了。
为了保留住这个对象,将它保存到一个变量中,以后可以随时调用:
>>> true = lambda :True >>> true() True
下面是更多的例子:
def add(x, y): return x + y 等价于 lambda x, y: x + y
def usuallyAdd2(x, y=2): return x+y 等价于 lambda x, y=2: x+y
def showAllAsTuple(*z): return z等价于lambda *z: z
>>> a = lambda x, y=2: x + y >>> a(3) 5 >>> a(3,5) 8 >>> a(0) 2 >>> a(0,9) 9 >>> >>> b = lambda *z: z >>> b(23, 'zyx') (23, 'zyx') >>> b(42) (42,)
二:filter()、map()、reduce()
这些函数提供了在python 中可以找到的函数式编程的特征。lambda 表达式可以很好的和这些函数相结合,因为这些函数都带了一个可执行的函数对象的参数。lambda 表达式提供了迅速创造这个参数的机制。
a:filter(function, iterable)
给定一个对象的序列和一个“过滤”函数,每个序列元素都通过这个过滤器进行筛选,保留函数返回为真的的对象。filter 函数为已知的序列的每个元素调用给定布尔函数function。将经过function处理返回True值的元素添加到一个列表中。返回的对象是一个从原始队列中“过滤后”的列表。
注意:如果iterable是一个字符串或者元组的话,则结果也是同样的类型,否则,结果为一个列表。如果function为None的话,则该函数表现为一个身份函数,也就是该函数会返回,iterable中所有值为True的元素组成的列表。
如果用纯python 编写filter(),它或许就像这样:
def filter(bool_func, seq): filtered_seq= [] for eachItem in seq: if bool_func(eachItem): filtered_seq.append(eachItem) return filtered_seq
例子如下:
>>> alist = [1,2,3,0,4,5] >>> filter(None, alist) [1, 2, 3, 4, 5] >>> filter(lambda x: True,'abcdefg') 'abcdefg' >>> filter(lambda x: True,(1,2,3,4,5)) (1, 2, 3, 4, 5)
注意,如果function不是None的话,则filter(function,iterable)等价于:[item for item in iterable if function(item)]。如果function为None的话,则它等价于[itemfor item in iterable if item]。比如求一个随机列表中所有奇数的方法:
allNums = [] for eachNum in range(9): allNums.append(randint(1,99)) print filter(lambda n: n%2, allNums)
等价于:
allNums = [] for eachNum in range(9): allNums.append(randint(1,99)) print [n for n in allNums if n%2]
b:map(function, iterable, ...)
将function应用到iterable中的每个元素上,并返回一个由返回值组成的列表。如果给出了多个iterable参数,则function必须具有相同数目的参数,并且该function会并行的迭代所有iterable中的元素。如果其中一个iterable比较短,则假设用None将其扩展。
如果function为None,则如果给出了多个iterable,那map返回一个元素为元组的列表,其中的元组包含所有iterable的相应元素。
该函数的结果只能是列表。
如果要用python 编写一个简单的只有一个iterable的map(),代码如下:
def map(func, seq): mapped_seq= [] for eachItem in seq: mapped_seq.append(func(eachItem)) return mapped_seq
例子如下:
>>> map((lambda x: x+2), [0, 1, 2, 3, 4, 5]) [2, 3, 4, 5, 6, 7] >>> map(None, (1,2,3,4),'abc') [(1, 'a'), (2, 'b'), (3, 'c'), (4, None)] >>> map(lambda x: x**2, range(6)) [0, 1, 4, 9, 16, 25] >>> map(lambda x, y: x + y, [1,3,5], [2,4,6]) [3, 7, 11] >>> map(lambda x, y: (x+y, x-y), [1,3,5],[2,4,6]) [(3, -1), (7, -1), (11, -1)] >>> map(None, [1,3,5], [2,4,6]) [(1, 2), (3, 4), (5, 6)]
c:reduce(function, iterable[, initializer])
function是有两个参数的函数,它会持续的对iterable中,从左到右的每个元素进行处理,从而会将iterable中所有元素reduce为一个值。比如reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]),将会计算((((1+2)+3)+4)+5)。左参数x是累计计算的结果,右参数y是iterable中的下一个元素。如果给定initializer参数值的话,则它会是第一个左参数,iterable中的第一个元素是第一个右参数。如果initializer没有给定值,则第一个左参数为iterable[0],第一个右参数为iterable[1]。
reduce的类Python代码实现如下:
def reduce(function, iterable, initializer=None): it = iter(iterable) if initializeris None: try: initializer= next(it) except StopIteration: raiseTypeError('reduce() of empty sequence with no initial value') accum_value =initializer for x in it: accum_value= function(accum_value, x) return accum_value
例子如下;
>>> print 'the total is:', reduce((lambda x,y:x+y), range(5)) the total is: 10
三:偏函数
偏函数类似于默认参数,可以降低函数调用的难度。举例如下:
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的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
注意到上面的新的int2函数,仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值:
>>> int2('1000000', base=10) 1000000
最后,创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数,当传入:int2 = functools.partial(int, base=2)实际上固定了int()函数的关键字参数base,也就是:
int2('10010')
相当于:
kw = { base: 2 }
int('10010', **kw)
当传入:
max2 = functools.partial(max, 10)
实际上会把10作为*args的一部分自动加到左边,也就是:
max2(5, 6, 7)
相当于:
args = (10, 5, 6, 7)
max(*args)
结果为10。
参考:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386819893624a7edc0e3e3df4d5d852a352b037c93ec000