为什么在Python 3中map返回map对象而不是列表?

11dmarpk  于 2023-02-01  发布在  Python
关注(0)|答案(4)|浏览(155)

我对了解new language design of Python 3.x很感兴趣。
在Python 2.7中,我非常喜欢map函数:

Python 2.7.12
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: [2, 3, 4]

然而,在Python 3.x中,情况发生了变化:

Python 3.5.1
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: <map at 0x4218390>

我理解“如何”,但我找不到“为什么”的参考。为什么语言设计者要做出这个选择,在我看来,这带来了很多痛苦。这是为了让开发人员在坚持列表解析方面角力吗?
IMO,列表可以自然地被认为是Functors;我一直被认为是这样想的

fmap :: (a -> b) -> f a -> f b
toe95027

toe950271#

我认为当generator expressions存在时map仍然存在的原因是它可以接受多个迭代器参数,这些参数都被循环并传递到函数中:

>>> list(map(min, [1,2,3,4], [0,10,0,10]))
[0,2,0,4]

这比使用zip稍微容易一些:

>>> list(min(x, y) for x, y in zip([1,2,3,4], [0,10,0,10]))

否则,它不会在生成器表达式上添加任何内容。

khbbv19g

khbbv19g2#

因为它返回一个迭代器,所以它省略了在内存中存储完整的列表。这样你就可以很容易地在将来迭代它,而不会给内存带来任何痛苦。可能你甚至不需要完整的列表,而是它的一部分,直到你的条件达到。
你会发现这个文档很有用,迭代器很棒。
表示数据流的对象。重复调用迭代器的__next__()方法(或将其传递给内置函数next())返回流中的连续项。当没有更多数据可用时,将引发StopIteration异常。此时,迭代器对象被耗尽,并且对它的__next__()方法的任何进一步调用只是再次引发StopIteration。迭代器需要有一个__iter__()方法,该方法返回迭代器对象本身,因此每个迭代器也是可迭代的,并且可以在大多数接受其他迭代器的地方使用。一个值得注意的例外是尝试多次迭代的代码。(例如list)在每次传递给iter()函数或在for循环中使用时都会产生一个新的迭代器。尝试使用迭代器执行此操作将只会返回与上一次迭代传递中使用的相同的已耗尽迭代器对象,使其看起来像空容器。

balp4ylt

balp4ylt3#

Guido回答这个问题here:“* 因为创建列表只会是浪费 *"。
他还说正确的转换是使用常规的for循环。
map()从2转换为3可能不仅仅是在它周围粘贴一个list( )的简单情况。
如果输入序列的长度不等,map()将在最短序列的终点处停止。为了与Python 2.x中的map()完全兼容,也可以将序列 Package 在itertools.zip_longest()中,例如:

map(func, *sequences)

变成

list(map(func, itertools.zip_longest(*sequences)))
7bsow1i6

7bsow1i64#

在Python 3中,许多函数(不仅仅是map,还有ziprange和其他函数)返回一个迭代器而不是完整的列表,你可能想要一个迭代器(例如,为了避免在内存中保存整个列表),或者你可能想要一个列表(例如,为了能够索引)。
然而,我认为Python 3中的变化的关键原因是,尽管使用list(some_iterator)将迭代器转换为列表是微不足道的,但相反的等价物iter(some_list)并没有达到预期的结果,因为完整的列表已经构建并保存在内存中。
例如,在Python 3中,list(range(n))工作得很好,因为构建range对象并将其转换为列表的成本很低,然而,在Python 2中,iter(range(n))并不保存任何内存,因为完整的列表是在构建迭代器之前由range()构建的。
因此,在Python 2中,需要单独的函数来创建迭代器而不是列表,例如imap用于map(尽管它们是not quite equivalent),xrange用于rangeizip用于zip,相比之下,Python 3只需要一个函数,因为如果需要的话,list()调用会创建完整的列表。

相关问题