Python 2、简单情况下map不等同于list comprehension;长度依赖
Python 2, map not equivalent to list comprehension in simple case; length dependent
在python 2 中,内置函数map
似乎在长度被覆盖时调用了__len__
。这是正确的 - 如果是这样,为什么我们要计算要映射的可迭代对象的长度? Iterables 不需要覆盖 length (e.g.),即使 length 没有被 iterable 预定义,map 函数也能工作。
地图已定义here;它确实指定在传递多个可迭代对象的情况下存在长度相关的功能。然而,
- 我对只有 一个可迭代对象被传递的情况感兴趣
- 即使传递了多个可迭代对象(不是我的问题),显式检查长度似乎是一个奇怪的设计选择,而不是仅仅迭代直到 运行 结束然后返回
None
我很担心,因为根据几个 1 2 非常高投票的问题,
map(f, iterable)
is basically equivalent to:
[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
的重载,它就不会正常运行。
在这种情况下,它会引发 AttributeError,因为可迭代对象是 None
。虽然单位行为很愚蠢,I see no requirement of invariance in the specification of len。当然,在对 len
的调用中不修改状态是一种很好的做法,但原因不应该是因为内置函数中的意外行为。在更现实的情况下,我的 len
函数可能很慢,我不希望担心它被 map
调用,或者它可能不是线程安全的,等等。
实现依赖?
由于 map
是一个内置函数,它可能具有规范之外的特定于实现的功能,但是 cpython implements it on line 918 of bltinmodule.c,它确实指出:
/* Do a first pass to obtain iterators for the arguments, and set len
* to the largest of their lengths.
*/
然后调用在Object/abstract.c中定义的_PyObject_LengthHint
,确实是在寻找被覆盖的len
。这并没有向我澄清这是否只是依赖于实现,或者如果我错过了一些原因 map
有目的地违背我的直觉寻找可迭代的长度。
(注意我没有在 python 3 中测试这个,这就是我指定 python 2 的原因。在 python3 中,映射 returns 一个生成器,所以至少我的一些说法是不正确的)
I am concerned because according to several 1 2 extremely
highly upvoted questions,
map(f, iterable)
is basically equivalent to:
[f(x) for x in iterable]
但我 运行 喜欢简单的例子,但事实并非如此。
但是调用_PyObject_LengthHint
应该是基本上等同于不调用它。对象的 __len__
或 __length_hint__
不应该像这样改变对象。您不妨说 map(f, iterable)
和 [f(x) for x in iterable]
是不等价的,因为如果 f
使用堆栈检查来确定它是否从 map
调用并执行不同的操作,则这两个代码段的行为不同。
至于为什么 map
这样做,它试图将列表预分配到正确的大小以避免需要调整列表的大小。调整大小只会减慢常数因子,但如果可以避免常数因子,为什么不呢?列表推导式在未来的 Python 版本中这样做是完全合理的。
我不太确定你在这里问什么。我假设你的问题是 "Why is the result of map(f, iterable)
not always equivalent to [f(x) for x in iterable]
?"
根据您的研究,很明显内置 map
函数存在一定程度的实现依赖性,这(虽然它确实有点奇怪)对于可迭代对象的自定义实现完全有意义.
__len__
的规范有点太松了,但这是一个很好的规范。在这样的方法中,对象状态的修改似乎应该被标记为非常不好。
但是,您对 map(f, iterable)
和 [f(x) for x in iterable]
之间等价性的解释似乎确实做出了不正确的假设。当然,在 f
、map
和 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
函数结果和列表理解不是一回事(如果你只看最后 2 个 eval)。哎呀,他们做完全不同的事情。这完全是上下文造成的。因此,除非明确说明,在大多数情况下,假设 map
和 iterable
的实现都没有规避 Python 中的机制是完全合理的。但是,如果有上下文,它是第一位的。
在python 2 中,内置函数map
似乎在长度被覆盖时调用了__len__
。这是正确的 - 如果是这样,为什么我们要计算要映射的可迭代对象的长度? Iterables 不需要覆盖 length (e.g.),即使 length 没有被 iterable 预定义,map 函数也能工作。
地图已定义here;它确实指定在传递多个可迭代对象的情况下存在长度相关的功能。然而,
- 我对只有 一个可迭代对象被传递的情况感兴趣
- 即使传递了多个可迭代对象(不是我的问题),显式检查长度似乎是一个奇怪的设计选择,而不是仅仅迭代直到 运行 结束然后返回
None
我很担心,因为根据几个 1 2 非常高投票的问题,
map(f, iterable)
is basically equivalent to:
[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
的重载,它就不会正常运行。
在这种情况下,它会引发 AttributeError,因为可迭代对象是 None
。虽然单位行为很愚蠢,I see no requirement of invariance in the specification of len。当然,在对 len
的调用中不修改状态是一种很好的做法,但原因不应该是因为内置函数中的意外行为。在更现实的情况下,我的 len
函数可能很慢,我不希望担心它被 map
调用,或者它可能不是线程安全的,等等。
实现依赖?
由于 map
是一个内置函数,它可能具有规范之外的特定于实现的功能,但是 cpython implements it on line 918 of bltinmodule.c,它确实指出:
/* Do a first pass to obtain iterators for the arguments, and set len * to the largest of their lengths. */
然后调用在Object/abstract.c中定义的_PyObject_LengthHint
,确实是在寻找被覆盖的len
。这并没有向我澄清这是否只是依赖于实现,或者如果我错过了一些原因 map
有目的地违背我的直觉寻找可迭代的长度。
(注意我没有在 python 3 中测试这个,这就是我指定 python 2 的原因。在 python3 中,映射 returns 一个生成器,所以至少我的一些说法是不正确的)
I am concerned because according to several 1 2 extremely highly upvoted questions,
map(f, iterable)
is basically equivalent to:
[f(x) for x in iterable]
但我 运行 喜欢简单的例子,但事实并非如此。
但是调用_PyObject_LengthHint
应该是基本上等同于不调用它。对象的 __len__
或 __length_hint__
不应该像这样改变对象。您不妨说 map(f, iterable)
和 [f(x) for x in iterable]
是不等价的,因为如果 f
使用堆栈检查来确定它是否从 map
调用并执行不同的操作,则这两个代码段的行为不同。
至于为什么 map
这样做,它试图将列表预分配到正确的大小以避免需要调整列表的大小。调整大小只会减慢常数因子,但如果可以避免常数因子,为什么不呢?列表推导式在未来的 Python 版本中这样做是完全合理的。
我不太确定你在这里问什么。我假设你的问题是 "Why is the result of map(f, iterable)
not always equivalent to [f(x) for x in iterable]
?"
根据您的研究,很明显内置 map
函数存在一定程度的实现依赖性,这(虽然它确实有点奇怪)对于可迭代对象的自定义实现完全有意义.
__len__
的规范有点太松了,但这是一个很好的规范。在这样的方法中,对象状态的修改似乎应该被标记为非常不好。
但是,您对 map(f, iterable)
和 [f(x) for x in iterable]
之间等价性的解释似乎确实做出了不正确的假设。当然,在 f
、map
和 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
函数结果和列表理解不是一回事(如果你只看最后 2 个 eval)。哎呀,他们做完全不同的事情。这完全是上下文造成的。因此,除非明确说明,在大多数情况下,假设 map
和 iterable
的实现都没有规避 Python 中的机制是完全合理的。但是,如果有上下文,它是第一位的。