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 接受一个函数和一个列表[8],并通过按顺序使用列表中的每个元素调用该函数来返回一个新列表。在本例中,该函数只是将每个元素乘以 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 接受一个文件名并返回一个元组 (名称, 扩展名)。因此,filenameToModuleName 是一个函数,它接受一个文件名并去除文件扩展名,只返回文件名。
2 调用 map 会获取 files 中列出的每个文件名,将其传递给函数 filenameToModuleName,并返回每个函数调用的返回值列表。换句话说,您将从每个文件名中去除文件扩展名,并将所有这些去除扩展名的文件名列表存储在 moduleNames 中。

正如您将在本章的其余部分中看到的那样,您可以将这种以数据为中心的思维方式一直扩展到最终目标,即定义和执行一个包含所有这些单独测试套件中的测试的测试套件。

脚注

[8] 再次强调,map 可以接受列表、元组或任何类似序列的对象。请参阅前面关于 filter 的脚注。