Skip to main content
 首页 » 编程设计

python之list(y) 行为在第一次调用时是 "wrong"

2025年05月04日19jyk

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

如果您调用 list(y) 并且 y 定义了一个 __len__ 方法,那么将调用 __len__。

1) 为什么?

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

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

3) 为什么列表长度会在所有后续调用中自行更正?

另请注意,调用“enumerate”不是问题所在。 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 

请您参考如下方法:

If you call list(y) and y has a len method defined, then len is called. why?

因为如果从一开始就知道,用最终长度构建结果列表比从一个空列表开始并一次添加一个项目要快。 __len__ 是而且必须是 100% 可靠的保证。

IOW,如果您不能返回可靠的值,请不要实现__len__ 等特殊方法。

至于第二个问题,您的 __len__ 实现已损坏,因为它们消耗 迭代器(并且不将其返回到原始状态)——所以它们不为后续的 .next 调用留下任何项目,因此 list 构造函数获得一个 StopIteration 并决定您的 __len__ 是只是片状(不幸的是,它比可怜的 list 可以猜到的更片状......!-)。