欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

Python入门学习之函数式编程

程序员文章站 2022-05-18 19:47:34
...
一 前言

  初次接触函数式编程是在学习分布式计算的时候,那时候对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)!