如何让list()在不调用__len__的情况下使用__iter__?

时间:2016-05-12 14:29:30

标签: python

我有一个同时使用.mtl__iter__方法的课程。后者使用前者来计算所有元素。

它的作用如下:

__len__

现在,如果我们采取例如正如预期的那样打印class A: def __iter__(self): print("iter") for _ in range(5): yield "something" def __len__(self): print("len") n = 0 for _ in self: n += 1 return n len的实例的长度:

iter

但如果我们致电>>> len(A()) len iter 5 ,则会同时调用list()__iter__

__len__

如果我们生成一个生成器表达式,它会按预期工作:

>>> list(A())
len
iter
iter
['something', 'something', 'something', 'something', 'something']

我认为>>> list(x for x in A()) iter ['something', 'something', 'something', 'something', 'something'] list(A())的工作方式相同,但他们没有。

请注意,它似乎首先调用list(x for x in A()),然后调用__iter__,然后遍历迭代器:

__len__

输出:

class B:
    def __iter__(self):
        print("iter")

        def gen():
            print("gen")
            yield "something"

        return gen()

    def __len__(self):
        print("len")
        return 1

print(list(B()))

如何让iter len gen ['something'] 不要调用list()以便我的实例的迭代器不被消耗两次?我可以定义例如一个__len__length方法然后会调用size,但这不是pythonic。

我尝试计算A().size()中的长度并对其进行缓存,以便后续调用__iter__不需要再次调用,但__len__调用list()而不启动迭代所以它不起作用。

请注意,在我的情况下,我处理非常大的数据集合,因此不能选择缓存所有项目。

2 个答案:

答案 0 :(得分:11)

可以肯定的是,list()构造函数检测到len()可用并调用它以便为列表预先分配存储空间。

您的实施几乎完全倒退。您正在使用__len__()来实现__iter__(),这不是Python所期望的。期望len()是一种快速有效的方法,可以提前确定的长度

我认为你不能说服list(A())不要打电话给len。正如您已经观察到的那样,您可以创建一个阻止len被调用的中间步骤。

如果序列是不可变的,你肯定应该缓存结果。如果你推测的项目数量太多,那就不止一次计算len了。

答案 1 :(得分:-2)

您不必实施__len__。对于可迭代的类,它只需要实现以下

  • __iter__,会在您的班级A&中返回iteratorgenerator。乙
  • __getitems__,只要在索引超出范围时引发IndexError

Blow代码仍有效:

class A:
    def __iter__(self):
        print("iter")
        for _ in range(5):
            yield "something"

print list(A())

哪个输出:

iter
['something', 'something', 'something', 'something', 'something']