迭代多个地图

Iteration over multiple maps

我有一个问题,关于 Python(3) 在计算多个地图时如何在内部循环。这是一个无意义的例子:

from random import randint

A = [randint(0,20) for _ in range(100)]
map1 = map(lambda a: a+1, A)
map2 = map(lambda a: a-1, map1)
B = list(map2)

因为 map() 产生惰性表达式,所以在调用 list(map2) 之前不会实际计算任何东西,对吗?

当它最终执行此计算时,它更类似于以下哪种方法?

循环方式一:

A = [randint(0,20) for _ in range(100)]
temp1 = []
for a in A:
    temp1.append(a+1)

B = []
for t in temp1:
    B.append(t-1)

循环方式二:

A = [randint(0,20) for _ in range(100)]
B = []
for a in A:
    temp = a+1
    B.append(temp-1)

或者它是以完全不同的方式计算的?

一般来说,map() 函数生成一个生成器,而生成器在明确要求之前不会生成任何输出或计算任何内容。将生成器转换为列表本质上类似于向它询问下一个元素,直到没有下一个元素为止。

我们可以在命令行上做一些实验,以了解更多信息:

>>> B = [i for i in range(5)]
>>> map2 = map(lambda b:2*b, B)
>>> B[2] = 50
>>> list(map2)
[0, 2, 100, 6, 8]

我们可以看到,即使我们在创建生成器之后修改了B,我们的更改仍然反映在生成器的输出中。因此,似乎 map 持有对创建它的原始可迭代对象的引用,并且仅在被要求时一次计算一个值。


在你的例子中,这意味着过程是这样的:

A = [2, 4, 6, 8, 10]
b = list(map2)
    b[0] --> next(map2) = (lambda a: a-1)(next(map1))
             --> next(map1) = (lambda a: a+1)(next(A)) 
                 --> next(A) = A[0] = 2
             --> next(map1) = 2+1 = 3
         --> next(map2) = 3-1 = 2
    ...

用人类的话说,map2的下一个值是通过询问map1的下一个值来计算的。 那个又是根据你最初设置的A计算出来的。

这可以通过对具有副作用的函数使用 map 来调查。一般来说,您不应该对实际代码执行此操作,但它可以用于调查行为。

def f1(x):
    print('f1 called on', x)
    return x

def f2(x):
    print('f2 called on', x)
    return x

nums = [1, 2, 3]
map1 = map(f1, nums)
map2 = map(f2, map1)
for x in map2:
    print('printing', x)

输出:

f1 called on 1
f2 called on 1
printing 1
f1 called on 2
f2 called on 2
printing 2
f1 called on 3
f2 called on 3
printing 3

因此,每个函数都在它可能被调用的最晚时间被调用; f1(2) 在循环完成数字 1 之前不会被调用。在循环需要映射中的第二个值之前,不需要对数字 2 执行任何操作。