'for'循环和map的区别

42

从标题上来看,是有区别的。现在将其应用到我的情况中:让我们考虑一个类 Dummy

class Dummy:
    def __init__(self):
        self.attached = []

    def attach_item(self, item):
        self.attached.append(item)

如果我使用这个:

D = Dummy()
items = [1, 2, 3, 4]
for item in items:
    D.attach_item(item)

我确实得到了D.attached = [1, 2, 3, 4]。但是如果我将函数attach_item映射到itemsD.attached仍然为空。

map(D.attach_item, items)

它在做什么?


可能是重复的问题:在Python中,当print语句在map函数中时不起作用 - user202729
另一个重复的问题是 https://dev59.com/B2gu5IYBdhLWcg3w6rU9 和 https://dev59.com/jHLYa4cB1Zd3GeqPY4qI 以及 https://stackoverflow.com/questions/47998941/is-the-python-map-function-a-value-returning-function,还有相关的 https://dev59.com/j3VC5IYBdhLWcg3wlyMo。 - Mazdak
3个回答

55

一个非常有趣的问题,它有一个有趣的答案。

map 函数返回一个可迭代的 Map 对象。由于 map 是惰性计算的,因此只有在迭代该对象时才会调用函数。

因此,如果您执行以下操作:

x = map(D.attach_item, items)
for i in x:
    continue

预期结果将显示。


8
如果你输入 list(x),意思是相同的。 - joaquin
@joaquin 那就是我被骗的地方。在我的脑海中,当执行 list(x) 时,你想要将 x 转换为列表。 - Mathieu
6
@Mathieu 您需要这么做。结果将会是一个由许多None组成的列表。但是,函数也会对Dummy对象的内部状态产生副作用,这正是您想要的。总的来说,当您更关注副作用而不是实际结果时,惰性求值并不是很直观。 - Ant
10
因此,我认为使用 map 来处理副作用是不好的编程风格。这样做很容易让人感到困惑,而且 OP 的问题也展现了这种困惑。 - marcelm

16

map 只创建一个迭代器,你需要通过迭代器将项目添加到D.attached中。就像这样:

D = Dummy()
items = [1, 2, 3, 4]
list(map(D.attach_item, items))

是的,在你的代码中不要这样做 :) 但是这个例子只是有助于理解。


1
应该先仔细阅读文档 x') 我以为我知道 map 函数在做什么,结果证明我错了... 谢谢! - Mathieu

9
引用文档的内容:

返回一个迭代器,该迭代器将函数应用于可迭代对象的每个元素,产生结果。

这意味着您必须收集迭代器,例如:
list(map(D.attach_item, items))

> [None, None, None, None]

嗯,奇怪。为什么是 None, None, ...

是的,你可以将任何循环转换成 map 语句,但这并不总是有用的。 Map 接受一个参数并对其进行某些操作(在大多数情况下),然后返回它,没有副作用!以下是一个示例:

def add(a):
    return a + 3
list(map(add, items))

> [4, 5, 6, 7]

当你将它与其他函数(如filter)结合使用时,真正的威力就会显现出来。

def add(a):
    return a + 3
def odd(a):
    return a % 2 == 1
list(map(add, filter(odd, items)))

> [4, 6]

之前我使用map时,总是在需要捕获函数返回值的情况下使用(例如你的例子)。因此我会应用list()。我没有注意到map只返回一个迭代器 :/ - Mathieu

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