我有兴趣了解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
答案 0 :(得分:28)
我认为当generator expressions也存在时,地图仍然存在 at 的原因是它可以采用多个迭代器参数,这些参数都被循环并传递给函数:
>>> 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]))
否则,它根本不会在生成器表达式上添加任何内容。
答案 1 :(得分:21)
因为它返回一个迭代器,所以它省略了在内存中存储完整大小的列表。因此,您可以在将来轻松迭代它,而不会给记忆带来任何痛苦。可能你甚至不需要一个完整的清单,而是它的一部分,直到达到你的状况。
你可以发现这个docs很有用,迭代器很棒。
表示数据流的对象。重复调用迭代器的
__next__()
方法(或将其传递给内置函数next()
)将返回流中的连续项。当没有更多数据可用时,会引发StopIteration
异常。此时,迭代器对象已用完,对__next__()
方法的任何进一步调用再次引发StopIteration
。迭代器需要有一个__iter__()
方法,它返回迭代器对象本身,因此每个迭代器也是可迭代的,并且可以在大多数接受其他迭代的地方使用。一个值得注意的例外是尝试多次迭代传递的代码。每次将容器对象(例如list
)传递给iter()
函数或在for循环中使用它时,它都会生成一个全新的迭代器。使用迭代器尝试此操作只会返回上一次迭代过程中使用的相同耗尽的迭代器对象,使其看起来像一个空容器。
答案 2 :(得分:13)
Guido回答了这个问题here:“因为创建列表只会浪费”。
他还说正确的转换是使用常规for
循环。
将map()
从2转换为3可能不仅仅是在其周围粘贴list( )
的简单情况。圭多还说:
“如果输入序列的长度不相等,map()
将在最短序列终止时停止。为了与Python 2.x中的map()
完全兼容,还要包装序列在itertools.zip_longest()
中,例如
map(func, *sequences)
成为
list(map(func, itertools.zip_longest(*sequences)))
“
答案 3 :(得分:10)
在Python 3中,许多函数(不只是setter
而是map
,zip
和其他函数)返回迭代器而不是完整列表。您可能需要一个迭代器(例如,为了避免将整个列表保存在内存中),或者您可能需要一个列表(例如,能够索引)。
但是,我认为Python 3更改的关键原因是,尽管使用range
将迭代器转换为列表是微不足道的,但反向等效list(some_iterator)
无法达到预期的结果,因为完整列表已经建立并保存在内存中。
例如,在Python 3 iter(some_list)
中工作得很好,因为构建list(range(n))
对象然后将其转换为列表的成本很低。但是,在Python 2中range
不保存任何内存,因为完整列表是在构建迭代器之前由iter(range(n))
构造的。
因此,在Python 2中,创建迭代器而不是列表需要单独的函数,例如range()
的{{1}}(尽管它们是not quite equivalent),{{1 {}为imap
,map
为xrange
。相比之下,Python 3只需要一个函数,因为range
调用会在需要时创建完整列表。