理解map函数

348

Python 2文档中写道:

内置函数:map(function, iterable, ...)

将函数应用于iterable的每个项,并返回结果列表。 如果传递了其他可迭代参数,则函数必须接受相同数量的参数,并同时应用于所有可迭代对象的项目。

如果一个可迭代对象比另一个短,那么假定它被延长到与None项一样长。

如果函数为None,则假定为恒等函数; 如果有多个参数,则map()返回由包含来自所有可迭代对象的相应项目的元组的列表(一种转置操作)组成的列表。

可迭代参数可以是序列或任何可迭代对象。 结果始终为列表。

这对制作笛卡尔积有什么作用?

content = map(tuple, array)

将元组放在那里有什么影响?我还注意到,如果没有map函数,输出是abc,而有了它,就是a,b,c

我想完全理解这个函数。参考定义也很难理解。太多花哨的废话了。


2
你实际上想要实现什么,为什么特别想使用 map - Kris Harper
3
@WebMaster,根据您贴出的文档中的第一句话:“将函数应用于可迭代对象的每个项目”,可以确定回答是肯定的。段落的其余部分涉及更复杂的情况,例如map(None, a, b, c)实际上等同于zip(a, b, c)。但实际上很少见到这种情况,因为这个zip调用是等效的。 - lvc
12
我很努力地学习 Python,但每当我打开 python.org 上的定义后,第一句话之后,我就什么也不懂了。好的,谢谢。 - Web Master
2
“tuple”是一个函数(其实比这更微妙,但它的行为类似于函数),它接受一个可迭代对象,并返回一个具有相同元素的元组 - 因此tuple([1, 2, 3])等同于(1, 2, 3)。对于map(tuple, array)array将是一个可迭代的可迭代对象(想象一下一个列表的列表),它会将每个内部列表转换为一个元组并返回。 - lvc
1
通常来说,任何函数文档的第一句话最为重要。如果你理解了这一点,你就掌握了其要义。其余部分则详细说明了行为,其中有些内容可能起初会有些晦涩,你可能需要遇到一些基于它的奇怪习语才能明白“哦,原来是这个意思!”但是一旦你对一些内置函数有了这样的灵光一现,你应该开始能够更容易地理解文档了。 - lvc
显示剩余9条评论
6个回答

505

map不是很符合Python风格。我建议改用列表推导式:

map(f, iterable)

基本等价于:

[f(x) for x in iterable]

map 本身无法执行笛卡尔积,因为其输出列表的长度始终与输入列表相同。但是您可以使用列表推导式来轻松执行笛卡尔积:

[(a, b) for a in iterable_a for b in iterable_b]

语法有些令人困惑 - 这基本上等同于:

result = []
for a in iterable_a:
    for b in iterable_b:
        result.append((a, b))

50
对于你展示的情况,我发现使用map比列表推导式要简洁得多。 - marbel
1
如何在属性中使用map?[v.__name__ for v in (object, str)]map等效方法是什么? - A Sz
@ASz 你觉得 map(lambda v: v.__name__, list) 怎么样? - Kilian
14
地图速度更快,因为它不会按迭代器长度调用函数... 调用函数会产生开销... 请看6:00的视频。 - anati
1
@anati 我认为map有时比列表推导式更快,有时不是,这恰恰是因为函数调用开销的缘故。特别是,我学到的启发式方法是,当使用map需要你引入一个额外的函数调用时,列表推导式更快?例如,我曾经相信map(lambda foo:foo.bar,my_list)foo.bar for foo in my_list慢,而且即使是map(operator.add, my_list_of_pairs)也比x + y for x,y in my_list_of_pairs慢,这正是因为额外的函数调用。 - mtraceur
显示剩余4条评论

96

map并不与笛卡尔积有任何关系,尽管我想象得出精通函数式编程的人可能会用map生成某种难以理解的笛卡尔积。

Python 3中的map相当于以下代码:

def map(func, iterable):
    for i in iterable:
        yield func(i)

在 Python 2 中唯一的区别是它会一次性生成完整的结果列表而不是使用yield.

尽管 Python 的惯例通常更喜欢使用列表推导式(或生成器表达式)来达到与调用 map 相同的结果,特别是如果您将 lambda 表达式作为第一个参数:

[func(i) for i in iterable]

作为您在问题评论中所要求的内容的示例 - "将字符串转换为数组",通过 'array' 您可能想要的是元组(tuple)或列表(list)(它们都有点像其他语言中的数组) -

 >>> a = "hello, world"
 >>> list(a)
['h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd']
>>> tuple(a)
('h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd')

如果你有一个字符串列表而不是单个字符串,那么在这里使用map的一种方式是,可以将它们全部转换为列表形式:

>>> a = ["foo", "bar", "baz"]
>>> list(map(list, a))
[['f', 'o', 'o'], ['b', 'a', 'r'], ['b', 'a', 'z']]

注意,在Python 2中map(list, a)是等效的,但在Python 3中,如果你想对其进行除了输入到for循环(或者只需要一个可迭代对象而不是序列的处理函数,例如sum)之外的任何操作,则需要调用list。但请再次注意,通常建议使用列表推导式:

>>> [list(b) for b in a]
[['f', 'o', 'o'], ['b', 'a', 'r'], ['b', 'a', 'z']]

map(fun x->(x,x))似乎不难理解...(虽然从map中获得真正的笛卡尔积是不可能的,但map产生的任何内容都是某种形式的列表)。 - Kristopher Micinski

40

map通过将函数应用于源中的每个元素来创建一个新列表:

xs = [1, 2, 3]

# all of those are equivalent — the output is [2, 4, 6]
# 1. map
ys = map(lambda x: x * 2, xs)
# 2. list comprehension
ys = [x * 2 for x in xs]
# 3. explicit loop
ys = []
for x in xs:
    ys.append(x * 2)

n元map相当于将输入的可迭代对象进行压缩,然后对该中间压缩列表中的每个元素应用转换函数。它不是笛卡尔积:

xs = [1, 2, 3]
ys = [2, 4, 6]

def f(x, y):
    return (x * 2, y // 2)

# output: [(2, 1), (4, 2), (6, 3)]
# 1. map
zs = map(f, xs, ys)
# 2. list comp
zs = [f(x, y) for x, y in zip(xs, ys)]
# 3. explicit loop
zs = []
for x, y in zip(xs, ys):
    zs.append(f(x, y))

我在这里使用了zip,但是当可迭代对象的大小不同时,map的行为实际上略有不同 - 如其文档所述,它会扩展可迭代对象以包含None


1
复杂,尝试消化这篇文章 - Web Master
1
@WebMaster 这有什么复杂的呢? - Jossie Calderon
在我看来,最好的答案。在示例中使用lambda作为函数非常清晰明了。 - sheldonzy
不幸的是,所有这些都不等价 - 列表推导式和显式循环的输出都是 [2,4,6],但 map 返回一个 map 对象 - 例如我得到这个:<map at 0x123a49978>,然后我必须将其强制转换为列表。 - leerssej

20

map()函数可以将相同的过程应用于可迭代数据结构中的每个项目,例如列表、生成器、字符串和其他内容。

让我们看一个例子:map()可以迭代列表中的每个项目并对每个项目应用一个函数,然后它将返回(给你)新列表。

想象一下您有一个函数,该函数接受一个数字,将1添加到该数字并将其返回:

def add_one(num):
  new_num = num + 1
  return new_num

你也有一个数字列表:

my_list = [1, 3, 6, 7, 8, 10]

如果您想增加列表中的每个数字,可以执行以下操作:

>>> map(add_one, my_list)
[2, 4, 7, 8, 9, 11]

注意:使用map()至少需要两个参数。第一个是函数名,第二个是类似于列表的东西。

接下来让我们看一些其他map()可以做的酷炫的事情。 map()可以获取多个可迭代对象(列表、字符串等),并将每个可迭代对象中的一个元素作为参数传递给函数。

我们有三个列表:

list_one = [1, 2, 3, 4, 5]
list_two = [11, 12, 13, 14, 15]
list_three = [21, 22, 23, 24, 25]

map()可以创建一个新列表,其中包含特定索引处元素的总和。

现在请记住,map()需要一个函数。这次我们将使用内置的sum()函数。运行map()将给出以下结果:

>>> map(sum, list_one, list_two, list_three)
[33, 36, 39, 42, 45]

请记住:
在Python 2中,map()函数将按照最长的列表进行迭代(遍历所有列表元素),并对较短的列表传递None值给函数,因此你的函数应该查找None并处理它们,否则会出现错误。在Python 3中,map()函数会在完成最短的列表后停止迭代。另外,在Python 3中,map()函数返回一个迭代器,而不是一个列表。


20

简单来说,你可以将map()想象成执行以下操作:

def mymap(func, lst):
    result = []
    for e in lst:
        result.append(func(e))
    return result

正如您所看到的,它接受一个函数和一个列表,并返回一个新列表,其中包含将函数应用于输入列表中每个元素的结果。我说“简化一下”,因为实际上map()可以处理多个可迭代对象:

  

如果传递了其他可迭代参数,则函数必须获取相同数量的参数,并并行地将其应用于所有可迭代项。如果一个可迭代对象比另一个短,则假定它将被扩展为None项目。

至于问题的第二部分:这在生成笛卡尔积中扮演了什么角色?嗯,map()可以用于生成像这样列表的笛卡尔积:

lst = [1, 2, 3, 4, 5]

from operator import add
reduce(add, map(lambda i: map(lambda j: (i, j), lst), lst))

...但说实话,使用product()函数是解决这个问题更简单和更自然的方法:

from itertools import product
list(product(lst, lst))
不管哪种方式,结果都是上述定义的 lst 的笛卡尔积。
[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5),
 (2, 1), (2, 2), (2, 3), (2, 4), (2, 5),
 (3, 1), (3, 2), (3, 3), (3, 4), (3, 5),
 (4, 1), (4, 2), (4, 3), (4, 4), (4, 5),
 (5, 1), (5, 2), (5, 3), (5, 4), (5, 5)]

10

Python3 - map(func, iterable)

虽然@BlooB有点提到了,但是完全没有提及的一件事是,map函数返回的是一个map对象而不是列表。在初始化和迭代的时间性能方面,这是一个很大的区别。考虑下面这两个测试。

import time
def test1(iterable):
    a = time.clock()
    map(str, iterable)
    a = time.clock() - a

    b = time.clock()
    [ str(x) for x in iterable ]
    b = time.clock() - b

    print(a,b)


def test2(iterable):
    a = time.clock()
    [ x for x in map(str, iterable)]
    a = time.clock() - a

    b = time.clock()
    [ str(x) for x in iterable ]
    b = time.clock() - b

    print(a,b)


test1(range(2000000))  # Prints ~1.7e-5s   ~8s
test2(range(2000000))  # Prints ~9s        ~8s

正如你所看到的,初始化地图函数几乎不需要时间。然而,遍历映射对象比简单地遍历可迭代对象需要更长的时间。这意味着传递给 map() 的函数直到在迭代中达到该元素时才应用于每个元素。如果您想要一个列表,请使用列表推导式。如果您计划在 for 循环中迭代并且会在某些时候中断,则使用 map。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接