繁体   English   中英

Python 2,map在简单情况下不等同于列表理解; 取决于长度

[英]Python 2, map not equivalent to list comprehension in simple case; length dependent

在python 2中,当长度被覆盖时,内置函数map似乎会调用__len__ 那是正确的吗-如果是这样, 为什么我们要计算可迭代映射的长度 迭代器不需要覆盖长度(例如) ,并且即使迭代器未预先定义长度,map函数也可以工作。

地图在这里定义; 确实指定了在传递多个可迭代对象的情况下具有与长度相关的功能。 然而,

  • 我对仅通过一个可迭代的情况很感兴趣
  • 即使传递了多个可迭代对象(不是我的问题),显式检查长度也似乎是一个奇怪的设计选择,而不是仅仅迭代直到用完然后返回None

我很担心,因为根据几个1 2极高评价的问题,

map(f, iterable)

基本上等同于:

[f(x) for x in iterable]

但是我遇到了一些不正确的简单示例。

例如

class Iterable:

    def __iter__(self):
        self.iterable = [1,2,3,4,5].__iter__()
        return self

    def next(self):
        return self.iterable.next()

   #def __len__(self):
   #     self.iterable = None
   #    return 5


def foo(x): return x

print( [foo(x) for x in Iterable()] )
print( map(foo,Iterable()) )

表现应有的表现,但是如果您取消对len的重载的评论,它就不len

在这种情况下,它会引发AttributeError,因为iterable为None 尽管单位行为很愚蠢,但我认为len的规范中没有不变性的要求 当然,最好不要在对len的调用中修改状态,但是原因不应该是因为内置函数中的行为异常。 在更现实的情况下,我的len函数可能只是很慢,我不希望担心被map调用,或者它不是线程安全的,等等。


实现依赖吗?

由于map是内置函数,因此它可能在规范之外具有特定于实现的功能,但是cpython在bltinmodule.c的第918行实现了它 ,实际上声明:

 /* Do a first pass to obtain iterators for the arguments, and set len * to the largest of their lengths. */ 

然后调用_PyObject_LengthHint ,该对象在Object / abstract.c中定义,并且实际上似乎在寻找被覆盖的len 这并不能告诉我这是否只是依赖于实现,或者是否缺少某种原因使map故意根据我的直觉寻找可迭代对象的长度。


(请注意,我尚未在python 3中进行过测试,这就是为什么我指定了python2。在python3中,map返回了一个生成器,因此至少我的一些说法不正确)

我很担心,因为根据几个1 2极高评价的问题,

map(f, iterable)

基本上等同于:

[f(x) for x in iterable]

但是我遇到了一些不正确的简单示例。

但调用_PyObject_LengthHint应该基本上等同于没有调用它。 对象的__len____length_hint__不应这样突变该对象。 您可能还说map(f, iterable)[f(x) for x in iterable]是不等价的,因为如果f使用堆栈检查来确定是否从map调用了它并且做了不同的事情,则这两个代码片段的行为会有所不同。

至于为什么map这样做,它试图将列表预先分配给正确的大小,以避免需要调整列表的大小。 仅将事物的大小减慢恒定因子,但是如果可以避免恒定因子,为什么不呢? 列表理解在将来的Python版本中做到这一点是完全合理的。

我不太确定你在这里问什么。 我将假设您的问题是“为什么map(f, iterable)并不总是等于[f(x) for x in iterable] ?”

从您的研究中可以很明显地看出,内置map函数中存在一定程度的实现依赖,(尽管确实有点奇怪)对于可迭代对象的自定义实现完全有意义。

__len__的规范有点太宽松了,但这是一个好主意。 似乎在这样的方法中,应将对对象状态的修改标记为非常糟糕。

但是,似乎您对map(f, iterable)[f(x) for x in iterable] map(f, iterable) [f(x) for x in iterable]之间的等价关系”的解释似乎做出了错误的假设。 当然,在fmapiterable的实现iterable评估的基础机制的情况下,这是正确的。 当他们这样做时,所有的赌注都关闭了。 基本上,上下文重要。 以这个为例:

def map(function, iterable):
    return None

def some_fun(x):
    return x + 1

a = [1,2,3]

>>> map(some_fun, a)
None
>>> [some_fun(x) for x in a]
[2, 3, 4]

显然,这里的map函数结果和列表理解不是同一件事(如果仅查看最后两个评估)。 哎呀,他们做的事情完全不同。 这完全是由于上下文。 因此,除非明确说明,否则在大多数情况下,完全假设mapiterable的实现都不会绕过Python中的机制是完全合理的。 但是,如果有上下文,则优先。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM