Python高级特性: 函数编程 lambda, filter,map,reduce
一、概述
Python是一门多范式的编程语言,它同时支持过程式、面向对象和函数式的编程范式。因此,在Python中提供了很多符合 函数式编程 风格的特性和工具。
以下是对 Python中的函数式编程 的简要总结,关于这一主题更全面的讨论可以参考 Functional Programming HOWTO。
二、lambda表达式(匿名函数)
除了 Python基础:函数 中介绍的 def语句,Python还提供了另外一种定义函数的方法: lambda表达式。
lambda表达式的语法如下:
lambda [arguments]: expression
与def语句类似,lambda表达式创建的函数:
- 也是可调用对象(接受0个或多个参数,返回一个值)
- 也是一等公民(first-class)
- 具有同样的 参数风格 和 作用域规则
- 也支持嵌套定义(def中的lambda,或lambda中的lambda)
但是lambda表达式与def语句之间,也存在很多显著的差异:
差异点 | 函数(lambda表达式) | 函数(def语句) |
---|---|---|
函数体 | 只能是单行表达式(expression) | 可以是任意复杂的语句(statement) |
函数返回值 | 返回值就是函数体中的表达式的求值结果 | 由函数体中的return语句指定 返回值 |
函数名 | 定义后直接返回函数对象(匿名函数) | 定义后自动为函数对象绑定函数名 |
函数定义位置 | 可以在任何允许函数对象出现的位置定义(支持即时定义,即时调用) | 只能在允许语句出现的位置定义(先定义,后调用) |
用途 | 多用于一次性使用的简单函数 | 适用于一切函数和类方法 |
以下是lambda表达式的简单示例:
# 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()、map()、reduce()
1、filter()
函数原型:filter(function, iterable)
说明:返回一个由iterable中的某些元素组成的列表,这些元素使得function返回True。若iterable为字符串(或元组),则返回字符串(或元组);否则,总是返回列表。如果function为None,则默认为恒等函数(identity function,类似 f(x) = x)。
示例:
# 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']
2、map()
函数原型: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)]
3、reduce()
函数原型: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中有一些其他特性就是借助闭包来实现的,比如 装饰器。
五、偏函数应用
1、基本用法
偏函数应用(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基础:函数式编程
从上面的示例可以看出,如果在编码过程中遇到了“多次用相同的参数调用一个函数”的场景,就可以考虑使用偏函数来固化这些相同的参数,进而简化函数调用。
2、等效实现
1)默认参数
在上述示例中,如果将函数title的定义改为def title(part, topic=u'Python基础')
也可以达到相同的效果。但是这种方式的不足之处也很明显:
- 需要修改已有函数title的定义
- 默认参数只能有一个固定值,定义后即不能更改
相比之下,偏函数具有很好的灵活性:既不用修改已有函数的定义,又可以为函数的参数固化不同的值。
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()和map()
1)filter()
列表解析可以完全代替filter():
- function不为None时:
[item for item in iterable if function(item)]
等价于filter(function, iterable)
- function等于None时:
[item for item in iterable if item]
等价于filter(None, iterable)
2)map()
在以下情况中,列表解析可以代替map():
- 只有一个iterable时
- function不为None:
[function(item) for item in iterable]
等价于map(function, iterable)
- function等于None:
[item for item in iterable]
等价于map(None, iterable)
- function不为None:
- 多个iterable长度相同时
- function不为None:
[function(*args) for args in zip(iter1, iter2, ...)]
等价于map(function, iter1, iter2, ...)
- function等于None:
zip(iter1, iter2, ...)
等价于map(None, iter1, iter2, ...)
- function不为None:
如果多个iterable具有不同的长度,那么列表解析就无法代替map()了。
2、生成器表达式
生成器表达式(Generator Expressions)与列表解析在语法和功能方面都非常相似。二者的根本差异是:生成器表达式返回一个 生成器,而列表解析返回一个列表。如下所示:
差异点 | 生成器表达式 | 列表解析 |
---|---|---|
表示方法 | (expr for item in iterable if cond_expr) | [expr for item in iterable if cond_expr] |
返回值 | 一个生成器 | 一个列表 |
与列表解析相比,生成器表达式具有 延迟计算(lazy evaluation)的特点,因此在使用内存上更有效。关于生成器表达式的实际案例,可以参考 Python核心编程(第二版) 中的 『8.13』 一节:『生成器表达式』。
Python高级特性: 函数编程 lambda, filter,map,reduce的更多相关文章
- Python内置函数之filter map reduce
Python内置函数之filter map reduce 2013-06-04 Posted by yeho Python内置了一些非常有趣.有用的函数,如:filter.map.reduce,都是对 ...
- lambda,filter,map,reduce
# lambda,filter,map,reduce from functools import reduce print('返回一个迭代器') print((x) for x in range(5) ...
- Python学习(五)函数 —— 内置函数 lambda filter map reduce
Python 内置函数 lambda.filter.map.reduce Python 内置了一些比较特殊且实用的函数,使用这些能使你的代码简洁而易读. 下面对 Python 的 lambda.fil ...
- Python之匿名函数(filter,map,reduce)
参考博客:Python匿名函数详解--http://blog.csdn.net/csdnstudent/article/details/40112803 Python内建函数之——filter,map ...
- Python中特殊函数和表达式lambda,filter,map,reduce
1.lambda:使用lambda表达式可以定义一个匿名函数 lambda表达式是一种简洁格式的函数.该表达式不是正常的函数结构,而是属于表达式的类型 (1)基本格式: lambda 参数,参数... ...
- [转]Python 中的 lambda,filter,map,reduce,apply
1. lambda 1. 基本形式: 函数名=lambda args1,args2,...,argsn:expression与C语言中的宏定义类似 2. Code isodd = lambda x: ...
- Python基础灬高阶函数(lambda,filter,map,reduce,zip)
高阶函数 lambda函数 关键字lambda表示匿名函数,当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便. lambda函数省略函数名,冒号前为参数,冒号后函数体. # ...
- Python中特殊函数和表达式 filter,map,reduce,lambda
1. filter 官方解释:filter(function or None, sequence) -> list, tuple, or string Return those items of ...
- Python 函数lambda(), filter(), map(), reduce()
1 filter filter(function, sequence):对sequence中的item依次执行function(item),将执行结果为True的item组成一个List/String ...
随机推荐
- Digital Roots—HDU1013 2016-05-06 10:25 85人阅读 评论(0) 收藏
Digital Roots Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) To ...
- QT5.4.0安装以及与VS2010整合安装---64bit操作系统解决方案
QT5.4.0安装以及与VS2010整合安装---64bit操作系统解决方案 注意,目前QT官网不能下载,必须提供注册,然后才可以下载. 网上不同版本安装的细节有差异,特将我的安装相关操作贴出来,希望 ...
- hdu 4861
http://acm.hdu.edu.cn/showproblem.php?pid=4861 两个人进行游戏,桌上有k个球,第i个球的值为1^i+2^i+⋯+(p−1)^i%p,两个人轮流取,如果Do ...
- SRM465
250pt: 给定50个整数点,范围-500-500之间.然后在这些点上选2个点作为中心,画边长为整数的正方形,并且正方形不能重叠(可以不平行),而且而且边长不同为不同方案.求有多少种方案.. 思路: ...
- PhantomJS快速入门-无界面浏览器
https://blog.csdn.net/libsyc/article/details/78199850 PhantomJS快速入门 本文简要介绍了PhantomJS的相关基础知识点,主要包括Pha ...
- 没那么难,谈CSS的设计模式
没那么难,谈CSS的设计模式 来源: 灵感的小窝 发布时间: 2016-09-09 16:46 阅读: 8949 次 推荐: 27 原文链接 [收藏] 什么是设计模式? 曾有人调侃, ...
- UvaOJ 10167
暴力搜索 #include<cstdio> struct node { int x; int y; }s[]; int main() { //freopen("input.txt ...
- Linux分区之parted命令
之前使用最多的分区命令无疑是fdisk了,大多数情况下fdisk可以满足日常工作上的需求,极个别情况就需要使用parted命令了,至于及个别情况就要从MBR和GPT说起. MBR主引导扇区 主 ...
- Web应用安全之点击劫持(CLICKJACKING)与X-FRAME-OPTIONS HEADER
点击劫持(clickjacking)与X-Frame-Options Header 文/玄魂 目录 前言... 1.1 点击劫持(clickjacking attacks)... 1.2 Frame ...
- WPF 分享一种背景动画效果
今天看微软的一个Samples,发现一个蛮好玩的背景样式,如下图所示: 风格比较卡哇伊. <Window x:Class="WPFSamplesTest.MainWindow" ...