Python 2,在简单的情况下映射不等同于列表推导; 长度依赖

en_*_*ght 6 python python-2.x

在python 2中,内置函数map似乎调用__len__时长度被覆盖.这是否正确 - 如果是这样,为什么我们计算迭代的长度来映射?Iterables不需要覆盖长度(例如),并且即使长度没有被iterable预定义,map函数也能工作.

地图在这里定义; 它确实指定在传递多个iterables的情况下存在与长度相关的功能.然而,

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

我因为根据几个很关注1 2极高upvoted的问题,

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()) )
Run Code Online (Sandbox Code Playgroud)

表现得如此,但如果你取消超载的话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.
 */
Run Code Online (Sandbox Code Playgroud)

然后调用_PyObject_LengthHint,在Object/abstract.c中定义,实际上似乎寻找覆盖len.这并没有向我澄清这是否只是依赖于实现,或者我是否缺少某种map有意识地根据我的直觉寻找迭代长度的理由.


(注意我没有在python 3中测试过这个,这就是为什么我指定了python 2.在python3中,map返回一个生成器,所以至少我的一些声明不是真的)

use*_*ica 2

我很担心,因为根据几个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]不等价的,因为 iff使用堆栈检查来确定它是否被调用map并执行不同的操作,这两个片段的行为不同。

至于为什么map这样做,它试图将列表预先分配到正确的大小,以避免需要调整列表的大小。调整大小只会使速度减慢一个常数因子,但如果您可以避免常数因子,为什么不呢?列表推导式在未来的 Python 版本中执行此操作是完全合理的。