16.4. 重识列表映射

你对使用列表解析映射列表的做法已经熟知。另一种方法可以完成同样的工作:使用内建 map 函数。它的工作机理和 filter 函数类似。

例 16.10. map 介绍

>>> def double(n):
...     return n*2
...     
>>> li = [1, 2, 3, 5, 9, 10, 256, -3]
>>> map(double, li)                       1
[2, 4, 6, 10, 18, 20, 512, -6]
>>> [double(n) for n in li]               2
[2, 4, 6, 10, 18, 20, 512, -6]
>>> newlist = []
>>> for n in li:                          3
...     newlist.append(double(n))
...     
>>> newlist
[2, 4, 6, 10, 18, 20, 512, -6]
1 map 接受一个函数和一个列表作为参数,[13] 并对列表中的每个元素依次调用函数返回一个新的列表。在这个例子中,函数仅仅是将每个元素乘以 2。
2 使用列表解析的方法你可以做到相同的事情。列表解析是在 Python 2.0 版时被引入的;而 map 则古老得多。
3 你如果坚持以 Visual Basic 程序员自居,通过 for 循环的方法完成相同的任务也完全可以。

例 16.11. map 与混合数据类型的列表

>>> li = [5, 'a', (2, 'b')]
>>> map(double, li)                       1
[10, 'aa', (2, 'b', 2, 'b')]
1 作为一个旁注,我想指出只要提供的那个函数能够正确处理各种数据类型,map 对于混合数据类型列表的处理同样出色。在这里,double 函数仅仅是将给定参数乘以 2,Python 则会根据参数的数据类型决定正确操作的方法。对整数而言,这意味着乘 2;对字符串而言,意味着把自身和自身连接;对于元组,意味着构建一个包括原始元组全部元素和原始元组组合在一起的新元组。

好了,玩够了。让我们来看一些真实代码。

例 16.12. regression.py 中的 map

    filenameToModuleName = lambda f: os.path.splitext(f)[0] 1
    moduleNames = map(filenameToModuleName, files)          2
1 正如你在 第 4.7 节 “使用 lambda 函数” 中所见,lambda 定义一个内联函数。也正如你在 例 6.17 “分割路径名” 中所见,os.path.splitext 接受一个文件名并返回一个元组 (name, extension)。因此 filenameToModuleName 是一个接受文件名,剥离出其扩展名,然后只返回文件名称的函数。
2 调用 map 将把 files 列出的所有文件名传递给 filenameToModuleName 函数,并且返回每个函数调用结果所组成的列表。换句话说,你剔除掉文件名的扩展名,并将剔除后的文件名存于 moduleNames 之中。

如你在本章剩余部分将看到的,你可以将这种数据中心思想扩展到定义和执行一个容纳来自很多单个测试套件的测试的一个测试套件的最终目标。

Footnotes

[13] 同前,我需要指出 map 可以接受一个列表、元组,或者一个像序列一样的对象。参见前面的关于 filter 的脚注。