我有一个同时具有a __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
Run Code Online (Sandbox Code Playgroud)
现在,如果我们采取例如它打印实例的长度len和iter,如预期:
>>> len(A())
len
iter
5
Run Code Online (Sandbox Code Playgroud)
但是,如果我们调用list()它同时呼吁__iter__和__len__:
>>> list(A())
len
iter
iter
['something', 'something', 'something', 'something', 'something']
Run Code Online (Sandbox Code Playgroud)
如果我们生成一个生成器表达式,它按预期工作:
>>> list(x for x in A())
iter
['something', 'something', 'something', 'something', 'something']
Run Code Online (Sandbox Code Playgroud)
我会假设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()))
Run Code Online (Sandbox Code Playgroud)
输出:
iter
len
gen
['something']
Run Code Online (Sandbox Code Playgroud)
我怎么能list()不调用,__len__以便我的实例的迭代器不被消耗两次?我可以定义例如一个length或一个size方法,然后一个人会调用,A().size()但那不是pythonic.
我试图计算长度__iter__并对其进行缓存,以便后续调用__len__不需要再次list()调用,而是调用__len__而不开始迭代,因此它不起作用.
请注意,在我的情况下,我处理非常大的数据集合,因此不能选择缓存所有项目.
agh*_*ast 11
可以肯定的是,list()构造函数检测到len()可用并调用它以便为列表预先分配存储空间.
你的实现几乎完全倒退了.您正在__len__()使用__iter__(),而不是Python期望的实现.期望是提前len()确定长度的快速,有效的方法.
我认为你不能说服list(A())不要打电话len.正如您已经观察到的那样,您可以创建一个阻止len被调用的中间步骤.
如果序列是不可变的,你肯定应该缓存结果.如果您推测的项目数量太多,那么计算len不止一次就没有意义.
| 归档时间: |
|
| 查看次数: |
2614 次 |
| 最近记录: |