函数式编程的一个特点就是,允许
1. 把函数本身作为参数传入另一个函数!
2. 返回一个函数!
python中主要有以下几方面的函数式编程方法,下面将从概念,作用和示例代码来说明.
一. 高阶函数(函数作为参数)
这个其实对应的是第一个特点,即“把函数本身作为参数传入另一个函数”,类似于C++中的”函数指针”,这样的技术很容易实现模板技术,最常用到的就是sort函数,看下面的代码,python中的sort对于int类型默认是从小到大的顺序,如果我们想自定义顺序呢?这个时候”高阶函数”的强大就体现出来了.
#!usr/bin/env python
import math
L = [0, -1, 8, 20, -99]
print sort(L, abs)
如上所示,abs这个函数被当做一个参数 传进了sort中作为sort的一个参数,为什么可以这样?其实准备来说,是把abs函数的函数名作为参数,在python里面,一个定义好的参数其实是一个存储在内存中的变量,比如下面的交互:
>>> def fun():
... print 'haha'
...
>>> fun
<function fun at 0x7faf4667f578>
>>>
具体的地址很可能跟我打印的不一样,不过实质都一样是一个地址,这也验证了上面“函数名是一个变量”的说法.
1. map和reduce
这两个内置函数是高阶函数应用的经典例子,直接看示例代码:
#!usr/bin/env python
def lala(x, y=2015):
return x % y
print map(lala, range(2000, 2030)
其运行结果是:[2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
#!usr/bin/env python
def plus(x, y):
return x * y
print reduce(plus, range(1, 5))
其结果是:24
,也就是1*2*3*4
其实从上面的代码和运行结果就可以猜到这两个函数的作用到底是什么了,它们都接受两个参数.第一个是一个函数名,第二个是一个列表,map将列表中的每一个元素作为函数的参数,返回每个函数返回值的列表,注意传入的函数的参数的个数,如果需要多个的话,那么对应的列表就要对应做变化.
至于reduce,对传入的函数的要求比较严格,需要恰好是偶数(有默认参数的不计在内),因为它做的事情,就是顺序地从列表执行,将上一次执行完的返回值与列表中的下一个元素再传进去计算,上面的代码的等效于
a = plus(1, 2) #首先是取第1,第2个元素
b = plus(a, 3) #然后返回值作为第一个参数,取下一个元素做为第二个参数
c = plus(b, 4) #同上
print c
至于这样的两个函数有什么作用呢?只能提供一点grammer sweet吗?答案是否定的!!!详情请看度娘
我觉得其中很有意义的一句话是“它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上”.
2. 过滤器filter
顾名思义,过滤器就是根据你提供的规则,将需要的数据保留下来,filter同样是接受两个参数,第一个是一个返回布尔值的函数,第二个是一个元素类型为该布尔函数的参数的列表.先看示例代码:
#!usr/bin/env python
import math
def is_prime(n):
if n <= 1: return False
for i in range(2, (int)(math.sqrt(n)+1)):
if n % i ==0: return False
return True
print filter(is_prime, range(1, 101))
其结果为: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
也就是1到100的所有素数.
二. 如何返回一个函数
1. lambda表达式(匿名方法)
python中支持简单lambda表达式,比如这样:
>>> f = lambda x : x * x
>>> f
<function <lambda> at 0x7faf4667fb18>
>>>
一目了然,没错,它就是用来产生匿名方法的!
“…lambda expressions and anonymous methods are really just two words for the same thing. The only thing that differs is, “What does the syntax look like ” And the lambda expressions are a further evolution of the syntax.But underneath, they do the same thing. They generate methods. You know, they’re in-line methods.”
——C#首席架构师Anders Hejlsberg
由此引出一个闭包的概念,它是怎么返回一个函数的呢?还是看代码来的直接吧:
#!usr/bin/env python
def f(x):
def fun():
return x * x
return fun
>>> f(1).__name__
'fun'
>>>
看到这里应该很清晰了吧,利用闭包,我们可以直接使用所在函数的参数保存当时的函数调用场景!!!至于有什么用,就见仁见智了,用到了,自然会明白其中的妙处,只是我自己现在也还不清楚T_T.
2. 装饰器
上面的匿名方法返回的是”原生的函数内容”,那么如果想在不改变源代码的情况下,增加函数的功能呢?有没有办法,装饰器就诞生了…用法直接看代码就知道了:
#!usr/bin/env python
def log(func):
def haha(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return haha
@log
def now(x):
print 'wo cao, %s' % x
>>> now('12点了')
call now():
wo cao, 12点了
>>>
用了@语法,表示拿log来装饰now函数.那log函数其实就是上面说过的”闭包”啦.这里只是简单地给函数加了一个”调用前声明”,自己要添加别的自然也是类似的方法.
值得注意的是:
a) “装饰的原理”
@log其实可以看成发生了这样一件事:now = log(now),也就是原来的now函数被覆盖了,变成了经过log闭包装饰过的一个函数.
b) 函数传参???
这里用了”万能的参数列表”:(*args, **kw),也就是无论你需要修饰的函数的参数列表是怎样的,都可以被修饰!
更详细的讲解见廖大大(LDD).
3. 偏函数
其实这个名字是从廖雪峰大大的教程那里看来的,感觉有点直译过头了,不过离散数学里面的偏序好像也是这么直译了,就不纠结命名问题了.它是干什么的呢?这次返回的又是什么样的函数?
当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
—— from LDD
看一个简单例子就能理解了:
#!usr/bin/env python
import functools #注意要import这个模块
#example code is from LDD
int2 = functools.partial(int, base=2)
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
将二进制转换为十进制,固定了base这个参数的值,相对于”全”,叫做”偏”也很直观了!
至此,python中的函数式编程的两大方面已经简单介绍完了.谢谢
阅读~碎觉( ⊙ o ⊙ )!