第一次调用时list(y)行为是“错误的”

时间:2010-09-16 02:38:33

标签: python list iterator

我有一个定义了__len__方法的迭代器。问题:

如果您调用list(y)并且y定义了__len__方法,则调用__len__

1)为什么?

在我的输出中,您会在第一次尝试时看到len(list(y))为0。如果查看列表输出,您将看到在第一次调用时,我收到一个空列表,在第二次调用时,我收到“正确”列表。

2)为什么它会返回一个长度为零的列表?

3)为什么列表长度在所有后续调用中都是正确的?

另请注意,调用“枚举”不是问题。 C类做同样的事情,但使用while循环并调用next()。

代码:

showcalls = False

class A(object):
    _length = None
    def __iter__(self):
        if showcalls:
            print "iter"
        self.i = 0
        return self        
    def next(self):
        if showcalls:
            print "next"
        i = self.i + 1
        self.i = i
        if i > 2:
            raise StopIteration
        else:
            return i

class B(A):
    def __len__(self):
        if showcalls:
            print "len"
        if self._length is None:
            for i,x in enumerate(self):
                pass
            self._length = i
            return i
        else:
            return self._length

class C(A):
    def __len__(self):
        if showcalls:
            print "len"
        if self._length is None:
            i = 0
            while True:
                try:
                    self.next()
                except StopIteration:
                    self._length = i
                    return i
                else:
                    i += 1
        else:
            return self._length

if __name__ == '__main__':
    a = A()
    print len(list(a)), len(list(a)), len(list(a))
    print
    b = B()
    print len(list(b)), len(list(b)), len(list(b))
    print
    c = C()
    print len(list(c)), len(list(c)), len(list(c))

输出:

2 2 2

0 2 2

0 2 2

1 个答案:

答案 0 :(得分:6)

  

如果你打电话给list(y)而你有一个   定义 len 方法,然后调用 len 。为什么呢?

因为构建具有最终长度的结果列表(如果从头开始已知)比以空列表开始并且一次附加一个项目更快。 __len__是,并且必须100%保证可靠。

如果您无法返回可靠值,请执行实施__len__等特殊方法。

关于第二个问题,__len__的实现被破坏了,因为它们消耗迭代器(并且不会将其恢复到原始状态) - 因此它们不会留下任何项目对于.next次调用,list构造函数会获得StopIteration并确定您的__len__只是片状(不幸的是,它比穷人list更难以猜测。 ..! - 。)