[英]Python 2, map not equivalent to list comprehension in simple case; length dependent
在python 2中,当长度被覆盖时,内置函数map
似乎会调用__len__
。 那是正确的吗-如果是这样, 为什么我们要计算可迭代映射的长度 ? 迭代器不需要覆盖长度(例如) ,并且即使迭代器未预先定义长度,map函数也可以工作。
地图在这里定义; 它确实指定了在传递多个可迭代对象的情况下具有与长度相关的功能。 然而,
None
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返回了一个生成器,因此至少我的一些说法不正确)
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]
之间的等价关系”的解释似乎做出了错误的假设。 当然,在f
, map
和iterable
的实现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
函数结果和列表理解不是同一件事(如果仅查看最后两个评估)。 哎呀,他们做的事情完全不同。 这完全是由于上下文。 因此,除非明确说明,否则在大多数情况下,完全假设map
和iterable
的实现都不会绕过Python中的机制是完全合理的。 但是,如果有上下文,则优先。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.