关于python:了解map函数

Understanding the map function

1
map(function, iterable, ...)

将函数应用于iterable的每个项,并返回结果列表。如果传递了额外的可Iterable参数,则函数必须接受如此多的参数,并并行应用于所有Iterable中的项。

如果一个iterable短于另一个iterable,则假定扩展时不包含任何项。

如果函数是None,则假定为标识函数;如果有多个参数,则map()返回一个由包含来自所有iterables的相应项的元组组成的列表(一种转置操作)。

iterable参数可以是序列或任何iterable对象;结果总是一个列表。

它在笛卡尔积的生成中扮演什么角色?

1
content = map(tuple, array)

在那里放一个元组有什么效果?我还注意到,没有map函数,输出是abc,有了它,输出是a, b, c

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


map不是特别的Python。我建议使用列表理解:

1
map(f, iterable)

基本上相当于:

1
[f(x) for x in iterable]

map本身不能做笛卡尔积,因为其输出列表的长度总是与其输入列表相同。你可以用列表理解来做一个笛卡尔积,不过:

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

语法有点混乱,基本上相当于:

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


map与笛卡尔积根本不相关,尽管我认为精通函数编程的人可能会想出一些不可能理解的方法,用map生成一个笛卡尔积。

python 3中的map相当于:

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

而在python 2中唯一的区别是,它将构建一个完整的结果列表来一次性返回所有结果,而不是返回yield

尽管python约定通常倾向于使用列表理解(或生成器表达式)来实现与对map的调用相同的结果,特别是当您使用lambda表达式作为第一个参数时:

1
[func(i) for i in iterable]

例如,您在对问题的注释中要求的内容-"将字符串转换为数组",通过"array",您可能需要一个元组或一个列表(它们的行为与其他语言的数组有些相似)-

1
2
3
4
5
 >>> 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,则可以单独列出所有字符串:

1
2
3
>>> 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调用。但也要再次注意,列表理解通常是首选的:

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


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

1
2
3
4
5
6
7
8
9
10
11
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相当于将输入iterables压缩在一起,然后对中间压缩列表的每个元素应用转换函数。它不是笛卡尔积:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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,但当iterables的大小不同时,map的行为实际上略有不同——正如其文档中所指出的,它将iterables扩展到包含None


简化一点,你可以想象map()做了这样的事情:

1
2
3
4
5
def mymap(func, lst):
    result = []
    for e in lst:
        result.append(func(e))
    return result

如您所见,它接受一个函数和一个列表,并返回一个新的列表,结果是将该函数应用于输入列表中的每个元素。我说"简化一点",因为事实上,map()可以处理多个不可计算的:

If additional iterable arguments are passed, function must take that many arguments and is applied to the items from all iterables in parallel. If one iterable is shorter than another it is assumed to be extended with None items.

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

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

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

…但说实话,使用product()是解决问题的一种简单自然的方法:

1
2
from itertools import product
list(product(lst, lst))

不管怎样,结果都是上面定义的lst的笛卡尔积:

1
2
3
4
5
[(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)]

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

让我们来看一个例子:map()可以迭代列表中的每个项目,并对每个项目应用一个函数,这样它就可以返回(返回)新列表。

假设您有一个函数接受一个数字,向该数字加1并返回它:

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

您还有一个数字列表:

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

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

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

注:至少map()需要两个参数。首先是一个函数名,然后是一个列表。

让我们看看map()能做些什么。map()可以接受多个iterables(列表、字符串等),并将一个元素从每个iterables传递给一个函数作为参数。

我们有三个列表:

1
2
3
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()会得到以下结果:

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

记得:在python 2中,map()将根据最长的列表进行迭代(遍历列表元素),并将None传递给函数以获取较短的列表,因此您的函数应该查找None并进行处理,否则会出错。在python 3中,map()将在以最短的列表结束后停止。另外,在python 3中,map()返回一个迭代器,而不是一个列表。


python3-地图(func,iterable)

有一件事没有完全提到(尽管@bloob有点提到过),那就是map返回一个map对象,而不是一个列表。当涉及到初始化和迭代的时间性能时,这是一个很大的区别。考虑这两个测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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函数几乎不需要任何时间。但是,遍历map对象比简单地遍历iterable花费的时间更长。这意味着传递给map()的函数在迭代中到达元素之前不会应用于每个元素。如果你想要一个列表,使用列表理解。如果计划在for循环中迭代并在某个点中断,那么使用map。