Python是一门多范式的编程语言,它同时支持过程式、面向对象和函数式的编程范式。所以,在Python中提供了不少符合 函数式编程 风格的特性和工具。html
如下是对 Python中的函数式编程 的简要总结,关于这一主题更全面的讨论能够参考 Functional Programming HOWTO。python
除了 Python基础:函数 中介绍的 def语句,Python还提供了另一种定义函数的方法: lambda表达式。express
lambda表达式的语法以下:编程
lambda [arguments]: expression
与def语句相似,lambda表达式建立的函数:markdown
可是lambda表达式与def语句之间,也存在不少显著的差别:闭包
差别点 | 函数(lambda表达式) | 函数(def语句) |
---|---|---|
函数体 | 只能是单行表达式(expression) | 能够是任意复杂的语句(statement) |
函数返回值 | 返回值就是函数体中的表达式的求值结果 | 由函数体中的return语句指定 返回值 |
函数名 | 定义后直接返回函数对象(匿名函数) | 定义后自动为函数对象绑定函数名 |
函数定义位置 | 能够在任何容许函数对象出现的位置定义(支持即时定义,即时调用) | 只能在容许语句出现的位置定义(先定义,后调用) |
用途 | 多用于一次性使用的简单函数 | 适用于一切函数和类方法 |
如下是lambda表达式的简单示例:app
# def语句 >>> def func(x, y): return x + y # 自动绑定函数名为func ... >>> func <function func at 0xb76eff7c> >>> func(1, 2) # 先定义,后调用 3 # lambda表达式 >>> lambda x, y: x + y # 匿名函数(直接返回函数对象) <function <lambda> at 0xb76ef0d4> >>> (lambda x, y: x + y)(1, 2) # 即时定义,即时调用 3 >>> f = lambda x, y: x + y # 手动绑定函数名 >>> f(1, 2) # 也能够先定义,后调用 3 >>> >>> ((lambda x: (lambda y: x + y))(1))(2) # 嵌套定义的lambda(较复杂,尽可能避免) 3
函数原型:filter(function, iterable)编程语言
说明:返回一个由iterable中的某些元素组成的列表,这些元素使得function返回True。若iterable为字符串(或元组),则返回字符串(或元组);不然,老是返回列表。若是function为None,则默认为恒等函数(identity function,相似 f(x) = x)。ide
示例:函数式编程
# for循环版本 >>> res = [] >>> for x in 'a1b2c3d4e5f6': ... if x.isalpha(): ... res.append(x) ... >>> res ['a', 'b', 'c', 'd', 'e', 'f'] # filter版本 s = 'a1b2c3d4e5f6' >>> filter((lambda x: x.isalpha()), s) # iterable为字符串,则返回字符串 'abcdef' >>> filter((lambda x: x.isalpha()), tuple(s)) # iterable为元组,则返回元组 ('a', 'b', 'c', 'd', 'e', 'f') >>> filter((lambda x: x.isalpha()), list(s)) # iterable为其余迭代对象,则返回列表 ['a', 'b', 'c', 'd', 'e', 'f'] >>> filter(None, list(s)) # function为None,则默认为恒等函数 ['a', '1', 'b', '2', 'c', '3', 'd', '4', 'e', '5', 'f', '6']
函数原型:map(function, iterable, ...)
说明:逐个以iterable中的元素为参数调用function,并返回结果的列表。若是存在多个iterable,则以最长的为准(其余不足的补None),逐个并行取出元素做为参数调用function(如map(function, iter1, iter2)会返回列表[function(iter1[0], iter2[0]), function(iter1[1], iter2[1]), ...])。若是function为None,则默认为恒等函数。
示例:
# for循环版本 >>> res = [] >>> for x in [1, 2, 3, 4, 5]: ... res.append(x ** 2) ... >>> res [1, 4, 9, 16, 25] # map版本 >>> map((lambda x: x ** 2), [1, 2, 3, 4, 5]) [1, 4, 9, 16, 25] >>> map(None, [1, 2, 3, 4, 5]) # function为None,则默认为恒等函数 [1, 2, 3, 4, 5] >>> map((lambda x, y: x + y), [1, 2, 3], [4, 5, 6]) # 存在多个iterable,则返回[1+4, 2+5, 3+6] [5, 7, 9] >>> map(None, [1, 2, 3], [4, 5]) # 以最长的iterable为准,其余不足的补None [(1, 4), (2, 5), (3, None)]
函数原型:reduce(function, iterable[, initializer])
说明:以累加方式逐个取出iterable中的元素做为参数调用(具备双参数的)function,从而最终将iterable简化为一个值(如reduce(function, [1, 2, 3])会返回function(function(1, 2), 3))。若是存在initializer,则在累加调用中,以它做为初始的第一个参数。function必须是可调用对象(不能为None)。
示例:
# for循环版本 >>> total = 0 >>> for x in [1, 2, 3, 4, 5]: ... total += x ... >>> total 15 # reduce版本 >>> reduce((lambda x, y: x + y), [1, 2, 3, 4, 5]) # 至关于((((1+2)+3)+4)+5) 15 >>> reduce((lambda x, y: x + y), [1, 2, 3, 4, 5], 10) # 带有initializer的reduce,至关于(((((10+1)+2)+3)+4)+5) 25 >>> sum([1, 2, 3, 4, 5], 10) # 等效于上面的reduce 25
闭包(closure)是一个内嵌函数,它可以记住其 外围做用域 中的全部名字,即便这个做用域 看起来 已经不在外围。
在如下示例中,内嵌函数action就是一个闭包:
>>> def maker(N): ... def action(x): ... return x * N ... return action ... >>> mul10 = maker(10) >>> mul10(3) 30 >>> mul10(5) 50
尽管函数调用mul10 = maker(10)
已经返回并退出了,但后续的mul10却可以记住整数10,从而计算入参的10倍数。
实际上,外围做用域(如函数maker对应的代码范围)中的全部名字(如参数N)都做为环境信息被绑定到了action函数上,所以每次调用action时均可以访问这些环境信息。特别地,能够经过特殊属性func_closure
来获取一个函数的自由变量绑定:
>>> def maker(N): ... def action(x): ... return x * N ... print(action.func_closure) # 打印出action函数的func_closure属性值 ... return action ... >>> N = 10 >>> print('int N: id = %#0x, val = %d' % (id(N), N)) # N的值为10(整数10的地址是0x8e82044) int N: id = 0x8e82044, val = 10 >>> mul10 = maker(N) # action.func_closure中含有整数10(即自由变量N) (<cell at 0x90e96bc: int object at 0x8e82044>,)
闭包的这种 可以记住环境状态 的特性很是有用,Python中有一些其余特性就是借助闭包来实现的,好比 装饰器。
偏函数应用(Partial Function Application)是一种简化函数调用的方式,主要表现为对函数的部分参数进行固化。
Python中的偏函数应用是借助 functools.partial 来完成的。例若有一个专用于生成文章标题的函数title:
>>> def title(topic, part): ... return topic + u':' + part ...
若是要为 『Python基础』 系列的多篇文章生成标题,能够有如下两种方式:
# 普通版本 >>> print title(u'Python基础', u'开篇') Python基础:开篇 >>> print title(u'Python基础', u'函数') Python基础:函数 >>> print title(u'Python基础', u'函数式编程') Python基础:函数式编程 # 偏函数版本 >>> from functools import partial >>> pybasic_title = partial(title, u'Python基础') >>> print pybasic_title(u'开篇') Python基础:开篇 >>> print pybasic_title(u'函数') Python基础:函数 >>> print pybasic_title(u'函数式编程') Python基础:函数式编程
从上面的示例能够看出,若是在编码过程当中遇到了“屡次用相同的参数调用一个函数”的场景,就能够考虑使用偏函数来固化这些相同的参数,进而简化函数调用。
1)默认参数
在上述示例中,若是将函数title的定义改成def title(part, topic=u'Python基础')
也能够达到相同的效果。可是这种方式的不足之处也很明显:
相比之下,偏函数具备很好的灵活性:既不用修改已有函数的定义,又能够为函数的参数固化不一样的值。
2)lambda表达式
使用 lambda表达式 也能够实现相似偏函数的功能,而且与默认参数不一样的是,能够针对不一样的参数值定义不一样的lambda表达式(由于lambda表达式一般是一次性使用的)。例如上述示例中的pybasic_title也能够实现为:
>>> pybasic_title = (lambda part: u'Python基础:' + part) >>> print pybasic_title(u'开篇') Python基础:开篇 >>> print pybasic_title(u'函数') Python基础:函数 >>> print pybasic_title(u'函数式编程') Python基础:函数式编程
可是,因为lambda表达式自己的限制(参考 『lambda表达式』 一节),在具备复杂函数的场景中,还得使用偏函数。
3)闭包
最后,使用 闭包 一样能够等效地实现偏函数的功能,而且与lambda表达式不一样的是,它没有任何限制场景。仍是上面的例子:
>>> def title(topic): ... def topic_title(part): ... return topic + u':' + part ... return topic_title ... >>> pybasic_title = title(u'Python基础') >>> print pybasic_title(u'开篇') Python基础:开篇 >>> print pybasic_title(u'函数') Python基础:函数 >>> print pybasic_title(u'函数式编程') Python基础:函数式编程
能够看出,这个闭包版本的惟一缺点是它须要对函数title进行从新定义(与默认参数的状况有些相似)。
总而言之,若是须要对 已有函数 进行参数固化,偏函数是最佳选择。
关于 列表解析(List Comprehensions),在 Python基础:序列(列表、元组) 中有过简单介绍。
这里主要强调两点:
1)filter()
列表解析能够彻底代替filter():
[item for item in iterable if function(item)]
等价于filter(function, iterable)
[item for item in iterable if item]
等价于filter(None, iterable)
2)map()
在如下状况中,列表解析能够代替map():
[function(item) for item in iterable]
等价于map(function, iterable)
[item for item in iterable]
等价于map(None, iterable)
[function(*args) for args in zip(iter1, iter2, ...)]
等价于map(function, iter1, iter2, ...)
zip(iter1, iter2, ...)
等价于map(None, iter1, iter2, ...)
若是多个iterable具备不一样的长度,那么列表解析就没法代替map()了。
生成器表达式(Generator Expressions)与列表解析在语法和功能方面都很是类似。两者的根本差别是:生成器表达式返回一个 生成器,而列表解析返回一个列表。以下所示:
差别点 | 生成器表达式 | 列表解析 |
---|---|---|
表示方法 | (expr for item in iterable if cond_expr) | [expr for item in iterable if cond_expr] |
返回值 | 一个生成器 | 一个列表 |
与列表解析相比,生成器表达式具备 延迟计算(lazy evaluation)的特色,所以在使用内存上更有效。关于生成器表达式的实际案例,能够参考 Python核心编程(第二版) 中的 『8.13』 一节:『生成器表达式』。