Python基础:09函数式编程

        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

posted @ 2015-05-31 18:16  gqtc  阅读(167)  评论(0编辑  收藏  举报