我有一个同时使用.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()
而不启动迭代所以它不起作用。
请注意,在我的情况下,我处理非常大的数据集合,因此不能选择缓存所有项目。
答案 0 :(得分:11)
可以肯定的是,list()
构造函数检测到len()
可用并调用它以便为列表预先分配存储空间。
您的实施几乎完全倒退。您正在使用__len__()
来实现__iter__()
,这不是Python所期望的。期望len()
是一种快速有效的方法,可以提前确定的长度。
我认为你不能说服list(A())
不要打电话给len
。正如您已经观察到的那样,您可以创建一个阻止len
被调用的中间步骤。
如果序列是不可变的,你肯定应该缓存结果。如果你推测的项目数量太多,那就不止一次计算len
了。
答案 1 :(得分:-2)