一 前言
初次接触函数式编程是在学习分布式计算的时候,那时候对map/reduce是不明觉厉,也没有懂多少原理方面的东西。Python中的函数式编程也算是初步了解一下map/reduce。所谓函数式编程,本质上是可以归结为面向过程的程序设计,但是它的思想很接近数学计算。它比一般的编程范式要更抽象,而且纯粹的函数式编程语言编写的函数是没有变量的,只要确定了输入,那也就确定了输出。它的另外一个特点就是把函数本身作为参数传入到另一个函数中,允许返回一个函数。
二 高阶函数(High-order Function)
在Python中,函数名本质上也是一个变量。我们可以将一个函数名赋值给一个变量,再通过这个变量来调用函数。在使用python面向过程的程序设计中,一个带有变量的函数是很普遍的设计,但是如果这个变量是一个函数,那么这个带有变量的函数我们就称之为高阶函数了。
一个简单的高阶函数示例:
def fun(n):
return n+1
def highorder(x, y, f):
return f(x)+f(y)
上面定义的highorder就是一个高阶函数,它是可以在参数中接收其他函数的函数。
三 Map/Reduce
有了上面的高阶函数基础,现在再来理解Map/Reduce就很容易了。Map函数接收两个参数,一个是函数,另一个是Iterable。Map将函数依次作用在Iterable的每一个元素上,并把结果作为新的Iterator返回。
看下面的示例:
def fun(n):
return n*2
m=map(fun, [1,2,3,4,5])PRint(m)
E:\Study\python>python hello_python.py
[2, 4, 6, 8, 10]
map把函数fun依次作用在列表的每一个元素上,就得到了[2,4,6,8,10]。
如果嫌定义一个fun函数比较麻烦,可以使用lambda来进行简化,如下:
m=map(lambda n:n*2, [1,2,3,4,5])
再看Reduce的用法。Reduce同Map一样,也是将一个函数依次作用在一个序列上,但是要求这个函数必须接收两个函数。Reduce再把函数作用在前两个参数的结果与下一个序列的元素上。
下面就用Reduce来实现一个序列求和运算,见下例:
def add(x,y):
return x+y
r=reduce(add, [1,2,3,4,5])
print(r)
E:\Study\python>python hello_python.py
15
它的lambda版本为:
r=reduce(lambda x,y:x+y, [1,2,3,4,5])
四 返回函数
在前面就已经表述过函数是可以被赋值给一个变量的,那么既然函数可以返回一个变量,当然也是可以返回一个函数的。别看返回变量和返回函数本质上区别不大,但是这种返回函数的机制却在应用中有着极大的作用。
来看下面的示例:
def wrapper(*param):
def calc():
sum=0
for x in param:
sum=sum+x
return sum
return calc;
f=wrapper(1,2,3,4,5)
print(f())
E:\Study\python>python hello_python.py
15
定义一个包裹函数wrapper,接收不定数量个参数。在调用此函数后,其会返回一个内部定义的函数,这个函数要在真正调用它时才会执行。另外还要注意的是,calc函数中访问的数据是由wrapper带进来的,并且这些参数会与calc被保存在一起,我们称之为“闭包”(closure)。
五 闭包(Closure)
初次接触闭包,对其并不是十分的理解。仍以四中的代码作为示例。
wrapper是一个函数,包括不定个数的参数param。比较特殊的地方是这个函数体中还定义了一个新的函数calc,这个新函数的函数体内正引用了一个外部函数wrapper的参数,也就是说,外部函数传递过来的参数已经和calc函数绑定到了一起,形成了一个新函数。我们可以把param看成是这个新函数的一个配置信息。配置信息如果不一样,那函数的输出当然也就不一样了。
为了更好的理解闭包,看以下代码示例:
def wrapper(conf):
def calc(n):
return conf+n
return calc
f1=wrapper(1)
f2=wrapper(2)
print(f1(100))
print(f2(100))
E:\Study\python>python hello_python.py
101
102
分析上述代码,调用wrapper(1)时会返回一个函数,并且这个函数的配置信息是conf的值为1。再调用wrapper(2)时会返回另外一个函数,并且这个函数的配置信息是conf的值为2。所以在随后的我们都传入100参数来调用f1和f2时得到的结果为101和102,其根本原因就在于两个函数的配置信息不一样。
值得我们注意的是,并不是外部函数的所有信息都会被内部函数做为配置信息,只有外部函数的参数才会被内部函数作为配置信息。至于外部函数的局部变量,就不会被做为配置信息了。
六 装饰器(Decorator)
发明Decorator的初衷是为了解决在不修改原有函数代码的情况下,在函数调用前后增加其他功能,比如打印日志等。Decorator本质上就是一个返回函数的高阶函数,看下面这个打印日志的decorator,代码如下:
def decorator(func):
def wrapper():
print("Before invoked:")
func()
print("After invoked:")
return wrapper
def func():
print("Func invoked:")
f=decorator(func)
f()
E:\Study\python>python hello_python.py
Before invoked:
Func invoked:
After invoked:
上述代码给func定义了一个装饰器,在调用这个装饰器时返回一个函数,在这个函数中加上需要的代码后再调用func。但这里有一个问题,那就是原来可以直接调用func,现如今却要调用f了。要解决这个问题很容易,因为在python中函数是可以赋值给一个变量的,只需要将f改成func就可以了。如下所示:
func=decorator(func)
func()
python中为实现这个机制提供了一个语法:@。在func前加上@decorator即可,相当于执行了func=decorator(func),这样就解决了使用相同的名字来调用增加功能后的代码。如下所示:
def decorator(func):
def wrapper():
print("Before invoked:")
func()
print("After invoked:")
return wrapper
@decorator
def func():
print("Func invoked:")
func()
另外还有如何给decorator增加参数以及如何修改wrapper的__name__属性为func的内容,这里就不讲述了。
七 偏函数(Partial Function)
何谓偏函数?偏函数就是给函数增加默认参数后的函数。在python中,可以使用functools.partial来生成一个函数的偏函数。拿python中的int()做示例,int()函数默认是按十进制转换,如果想生成一个按8进制转换的偏函数,可以如下实现:
print(int('12345'))
int8=functools.partial(int, base=8)
print(int8('12345'))
八 总结
在这篇文章中,主要讲述了函数式编程中的几个基本概念。个人感觉最难理解的就是Decorator了,特别是其中的所谓配置信息。如有错误之处,敬请留言!!!
以上就是Python入门学习之函数式编程的内容,更多相关文章请关注PHP中文网(www.php.cn)!