[TOC]
### 引入
Python内建了`map()`和`reduce()`函数。
如果你读过`Google`的那篇大名鼎鼎的论文`“MapReduce: Simplified Data Processing on Large Clusters`”,你就能大概明白`map/reduce`的概念。
### map()
我们先看`map`。`map()`函数接收两个参数,一个是函数,一个是`Iterable`,`map`将传入的函数依次作用到序列的每个元素,并把结果作为新的`Iterator`返回。
举例说明,比如我们有一个函数f(x)=$$x^{2}$$,要把这个函数作用在一个`list [1, 2, 3, 4, 5, 6, 7, 8, 9]`上,就可以用`map()`实现如下:
![image](http://www.liaoxuefeng.com/files/attachments/0013879622109990efbf9d781704b02994ba96765595f56000/0)
现在,我们用Python代码实现:
~~~
>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
~~~
`map()`传入的第一个参数是`f`,即函数对象本身。由于结果`r`是一个`Iterator`,`Iterator`是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个`list`。
你可能会想,不需要`map()`函数,写一个循环,也可以计算出结果:
~~~
L = []
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
L.append(f(n))
print(L)
~~~
的确可以,但是,从上面的循环代码,能一眼看明白“把`f(x)`作用在`list`的每一个元素并把结果生成一个新的`list`”吗?
所以,`map()`作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=$$x^{2}$$,还可以计算任意复杂的函数,比如,把这个 `list` 所有数字转为字符串:
~~~
>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
~~~
只需要一行代码。
### reduce()
再看`reduce`的用法。`reduce`把一个函数作用在一个序列`[x1, x2, x3, ...]`上,这个函数必须接收两个参数,`reduce`把结果继续和序列的下一个元素做累积计算,其效果就是:
~~~
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
~~~
比方说对一个序列求和,就可以用`reduce`实现:
~~~
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
~~~
当然求和运算可以直接用Python内建函数`sum()`,没必要动用`reduce`。
但是如果要把序列`[1, 3, 5, 7, 9]`变换成整数`13579`,`reduce`就可以派上用场:
~~~
>>> from functools import reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579
~~~
这个例子本身没多大用处,但是,如果考虑到字符串`str`也是一个序列,对上面的例子稍加改动,配合`map()`,我们就可以写出把`str`转换为`int`的函数:
~~~
>>> from functools import reduce
>>> def fn(x, y):
... return x * 10 + y
...
>>> def char2num(s):
... return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
...
>>> reduce(fn, map(char2num, '13579'))
13579
~~~
整理成一个`str2int`的函数就是:
~~~
from functools import reduce
def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
return reduce(fn, map(char2num, s))
~~~
还可以用`lambda`函数进一步简化成:
~~~
from functools import reduce
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
def str2int(s):
return reduce(lambda x, y: x * 10 + y, map(char2num, s))
~~~
也就是说,假设Python没有提供`int()`函数,你完全可以自己写一个把字符串转化为整数的函数,而且只需要几行代码!
`lambda`函数的用法在后面介绍。
### 练习
利用`map()`函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:`['adam', 'LISA', 'barT']`,输出:`['Adam', 'Lisa', 'Bart']`:
~~~
# -*- coding: utf-8 -*-
def normalize(name):
pass
# 测试:
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print(L2)
~~~
#### 参考源码
~~~
# -*- coding: utf-8 -*-
def normalize(name):
return name[0:1].upper()+name[1:].lower()
# 测试:
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print(L2)
~~~
### 练习 2
Python提供的sum()函数可以接受一个list并求和,请编写一个prod()函数,可以接受一个list并利用reduce()求积:
~~~
# -*- coding: utf-8 -*-
from functools import reduce
def prod(L):
pass
print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9]))
~~~
#### 参考源码
~~~
# -*- coding: utf-8 -*-
from functools import reduce
def prod(L):
return reduce(lambda x,y:x * y,L)
print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9]))
~~~
>[info](制书作者)这里解释一下 lambda 函数,如上面的 (lambda x,y:x * y) 的意思是:
定义了一个两个参数的函数,参数分别为: x,y 其函数体为 x * y
可以看成 Python 的代码如下:
~~~
def f(x,y)
return x * y
~~~
### 练习 3
利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:
~~~
# -*- coding: utf-8 -*-
from functools import reduce
def str2float(s):
pass
print('str2float(\'123.456\') =', str2float('123.456'))
~~~
#### 参考源码
~~~
# -*- coding: utf-8 -*-
from functools import reduce
def str2float(s):
//通过字符串的 split(s) :根据 s 来分隔字符串,返回字符串数组,比如这里的 通过`'.'`就分隔成了 '123','456'
sArr = s.split('.')
leng = len(sArr[1])
t = 1.0
while leng > 0:
t*=10
leng-=1
//把字符串数组转化成 float list
L = list(map(float,sArr))
//使用 lambda 函数计算合成返回
return reduce(lambda x,y:x+y,[L[0],L[1]/t])
print('str2float(\'123.456\') =', str2float('123.456'))
~~~
- Python教程
- Python简介
- 安装Python
- Python解释器
- 第一个 Python 程序
- 使用文本编辑器
- Python代码运行助手
- 输入和输出
- 源码
- learning.py
- Python基础
- 数据类型和变量
- 字符串和编码
- 使用list和tuple
- 条件判断
- 循环
- 使用dict和set
- 函数
- 调用函数
- 定义函数
- 函数的参数
- 递归函数
- 高级特性
- 切片
- 迭代
- 列表生成式
- 生成器
- 迭代器
- 函数式编程
- 高阶函数
- map/reduce
- filter
- sorted
- 返回函数
- 匿名函数
- 装饰器
- 偏函数
- Python函数式编程——偏函数(来自博客)
- 模块
- 使用模块
- 安装第三方模块
- 面向对象编程
- 类和实例
- 访问限制
- 继承和多态
- 获取对象信息
- 实例属性和类属性
- 面向对象高级编程
- 使用__slots__
- 使用@property
- 多重继承
- 定制类
- 使用枚举类
- 使用元类
- 错误、调试和测试
- 错误处理
- 调试
- 单元测试
- 文档测试
- IO编程
- 文件读写
- StringIO和BytesIO
- 操作文件和目录
- 序列化
- 进程和线程
- 多进程
- 多线程
- ThreadLocal
- 进程 vs. 线程
- 分布式进程
- 正则表达式
- 常用内建模块
- datetime
- collections
- base64
- struct
- hashlib
- itertools
- contextlib
- XML
- HTMLParser
- urllib
- 常用第三方模块
- PIL
- virtualenv
- 图形界面
- 网络编程
- TCP/IP简介
- TCP编程
- UDP编程
- 电子邮件
- SMTP发送邮件
- POP3收取邮件
- 访问数据库
- 使用SQLite
- 使用MySQL
- 使用SQLAlchemy
- Web开发
- HTTP协议简介
- HTML简介
- WSGI接口
- 使用Web框架
- 使用模板
- 异步IO
- 协程
- asyncio
- async/await
- aiohttp
- 实战
- Day 1 - 搭建开发环境
- Day 2 - 编写Web App骨架
- Day 3 - 编写ORM
- Day 4 - 编写Model
- Day 5 - 编写Web框架
- Day 6 - 编写配置文件
- Day 7 - 编写MVC
- Day 8 - 构建前端
- Day 9 - 编写API
- Day 10 - 用户注册和登录
- Day 11 - 编写日志创建页
- Day 12 - 编写日志列表页
- Day 13 - 提升开发效率
- Day 14 - 完成Web App
- Day 15 - 部署Web App
- Day 16 - 编写移动App
- FAQ
- 期末总结