python map对象原理,为什么map在Python 3中返回地图对象而不是列表?

我有兴趣了解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]:

我理解如何,但我找不到为什么的参考。 为什么语言设计师会做出这样的选择,在我看来,这会引入很多痛苦。 这是为了让开发人员坚持列表理解吗?

IMO,list自然可以被认为是Functors; 而且我一直被认为以这种方式思考:

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

理由应该与我们使用生成器而不是列表推导的原因相同。通过使用惰性评估,我们不需要在内存中保留大量内容。在这里查看接受的答案:stackoverflow.com/questions/1303347/

在C#中,"地图"被懒惰地评估。我打赌它与Python 3的地图或生成器表达式相同。这节省了内存。

你能解释为什么这给你带来"痛苦"吗?

你是否真的更喜欢2.7以上[x+1 for x in [1,2,3]]的地图?

我认为这是因为多年的使用表明map的最常见用途只是迭代结果。当你不需要它时建立一个列表是低效的,所以开发人员决定使map懒惰。这里有很多可以获得的性能而且不会丢失很多(如果你需要一个列表,只需要一个...... list(map(...)))。

好吧,我发现有趣的是,它不是保留Functor模式并提供List的惰性版本,而是以某种方式决定在映射时强制对列表进行惰性求值。我宁愿有权做出我自己的选择,又名发电机 - >地图 - >发电机或列表 - >地图 - >列表(由我来决定)

@NoIdeaHowToFix这个,实际上取决于你,如果你需要整个列表,只需将它转换成一个列表,就像地狱一样容易

@NoIdeaHowToFixThis:您有这个选择,您可以使用生成器或列表表达式,或使用list(map(...))。

嗯,是的,当然,我可以将迭代器转换回列表,但这会污染我的代码(个人意见)。 list(map(..))而不是map(..),这是我的痛苦。 @RemcoGerlich:嗯,我确实已经选择了一个玩具示例,但有些情况我觉得使用地图更方便列表理解(个人意见)

无论如何,我认为我们不应该把它作为辩论的地方。我想你们都帮助我理解了设计选择和策略。谢谢!

@Chris_Rands:在Python 2中你有生成器表达式:(f(x) for x in xs)

map()的整个"懒惰"是值得商榷的,因为它既不是可订阅的,也不会在迭代两次时产生相同的结果(尝试:m = map(str, [1, 2,3]); print(list(m)); print(list(m)))。

@Chris_Rands我没有声称没有列表理解;)。我忘了提及itertools.imap,这可能更适合你的目的。

@abukaj好吧,python不是Haskell。 Python没有引用透明性,因此您不应期望任何表达式计算两次产生相同的结果。

@NoIdeaHowToFixThis所以你需要引入一个全新的懒惰数据类型呢?这仍然没有提供python3映射的作用:懒惰的数据结构在使用时仍然消耗内存。 python3的地图可以在恒定的空间中迭代。如果你有类似的东西,这很重要:for k in map(func, itertools.count()): if predicate(k): break。使用惰性数据结构,这将占用越来越多的内存,直到OOM终止进程。 Python3映射在该循环期间使用O(1)内存。

@Bakuriu - 好吧,如果这是关注点,那么应该有一个Stream或者其他东西。还在学习python。我想知道为什么地图会做它的作用。在其他编程语言中,行为(内存占用,懒惰,操作成本等)是数据结构设计的一部分,然后是数据结构。即:map只映射数据结构:返回类型是您输入的内容。

@Bakuriu我没想到。我声称Python 3中map()的输出不是Python 2的map()的延迟评估输出。我的观点是它返回一个迭代器,而不是一个延迟评估的列表。解决第二条注释,在Python 2中,您可以在迭代时使用itertools.imap()来节省内存。

@NoIdeaHowToFixThis那是因为在那些语言中map是类/接口的方法。在python map中只是一个接受一个(或多个)可迭代并生成可迭代的函数。 map不适用于树数据结构,但仅适用于严格的顺序数据。不要将python的map视为Functor操作。

@abukaj我从未说过结果是列表的懒惰版本。结果以懒惰的方式产生,例如,按需,但结果本身是一个迭代器对象,并充当迭代器。术语懒惰仅仅意味着"在需要时计算"而不是结果应该是真实列表。

@Bakuriu然后我们同意:)。我的评论是对索赔的回应,这种改变只是将懒惰引入map()。我使用map()(2.7)来获得序列的"稳定"图像,而不是一次性迭代,所以对我来说,改变远不止是内置的懒惰。

@NoIdeaHowToFixThis:"返回类型就是你所提供的" - 这样的设计将无法接受任意的迭代。 map(str, xrange(5))无法返回xrange,map(int, some_file)无法返回file。

我刚刚遇到这个令人困惑的错误消息'map' object does not support item assignment而没有太多运气。所以这里是搜索引擎....

我认为,当生成器表达式也存在时,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]))

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

我认为,如果我们强调强调列表理解更加抒情,而语言设计者想强调这一点,我认为这是最现场的答案。 @vishes_shell在语言设计方面不够专注。

如果两个列表的长度不相等,则在Python 2和3中生成不同的结果。在python 2和python 3中尝试c = list(map(max, [1,2,3,4], [0,10,0,10, 99]))。

以下是从python3中完全删除地图的原始计划的参考:artima.com/weblogs/viewpost.jsp?thread = 98196

嗯当我在列表中包装地图时有多奇怪,我得到一个1元素列表的列表。

因为它返回一个迭代器,所以省略了将完整大小的列表存储在内存中。因此,您可以在将来轻松迭代它,而不会给记忆带来任何痛苦。可能你甚至不需要一个完整的清单,但它的一部分,直到你的状况达到。

你可以发现这个文档很有用,迭代器很棒。

An object representing a stream of data. Repeated calls to the iterator’s __next__() method (or passing it to the built-in function next()) return successive items in the stream. When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its __next__() method just raise StopIteration again. Iterators are required to have an __iter__() method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as a list) produces a fresh new iterator each time you pass it to the iter() function or use it in a for loop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.

Guido在这里回答这个问题:"因为创建一个列表只会浪费"。

他还说正确的转换是使用常规的for循环。

将map()从2转换为3可能不仅仅是在其周围粘贴list( )的简单情况。圭多还说:

"如果输入序列长度不相等,则map()将在序列中最短的序列处停止。为了与Python 2.x中的map()完全兼容,还要将序列包装在itertools.zip_longest()中,例如,

map(func, *sequences)

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

"

Guido评论是针对函数的副作用调用map(),而不是作为函子使用。

zip_longest的转换是错误的。您必须使用itertools.starmap才能使其等效:list(starmap(func, zip_longest(*sequences)))。那是因为zip_longest产生元组,所以func将接收单个n -uple参数而不是n不同的参数,就像调用map(func, *sequences)时一样。

在Python 3中,许多函数(不仅仅是map,而是zip,range等)返回迭代器而不是完整列表。您可能需要一个迭代器(例如,为了避免将整个列表保存在内存中),或者您可能需要一个列表(例如,能够索引)。

但是,我认为Python 3更改的关键原因是虽然使用list(some_iterator)将迭代器转换为列表是微不足道的,但是反向等效iter(some_list)并没有达到预期的结果,因为已经构建了完整列表并留在记忆中。

例如,在Python 3 list(range(n))中工作得很好,因为构建range对象然后将其转换为列表的成本很低。但是,在Python 2中,不会保存任何内存,因为在构建迭代器之前,完整列表是由range()构造的。

因此,在Python 2中,需要单独的函数来创建迭代器而不是列表,例如map用于map(尽管它们不完全等效),xrange用于range,izip用于zip。相比之下,Python 3只需要一个函数,因为list()调用会根据需要创建完整列表。

Python 2.7中的AFAIK也可以从itertools返回迭代器。此外,我不会将迭代器视为惰性列表,因为列表可以多次迭代并随机访问。

@abukaj好的谢谢,我编辑了我的答案,试图让自己更清楚

地图对象如何成为迭代器?它没有next()方法......

@IgorRivin你是什么意思? Python 3 map对象有一个next()方法。 Python 3 range范围对象不是我所知道的严格迭代器

@Chris_Rands在我的Anaconda发行版python 3.6.2中,做foo = map(lambda x: x, [1, 2, 3])返回一个地图对象foo。执行foo.next()时会返回错误:'map' object has no attribute 'next'

@IgorRivin您没有使用Python 3语法,请尝试next(foo)或foo.__next__()

@Chris_Rands谢谢!我不确定如何foo .__ next __()是对foo.next()的改进,但无论如何......

@IgorRivin:以__开头和结尾的方法保留给Python;没有这个保留,你就有了区分next只是一个方法(它们不是真正的迭代器)和迭代器的东西的问题。在实践中,您应该跳过这些方法并使用next()函数(例如next(foo)),该函数适用于2.6之上的每个Python版本。这与使用len(foo)的方式相同,即使foo.__len__()可以正常工作; dunder方法通常不是直接调用,而是隐含地作为其他操作的一部分。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值