这个bug的原始上下文是一段太大而无法在这样的问题中发布的代码.我不得不将这段代码缩小到仍然显示错误的最小片段.这就是为什么下面显示的代码有些奇怪.
在下面的代码中,该类Foo
可能被认为是一种令人费解的方式来获得类似的东西xrange
.
class Foo(object):
def __init__(self, n):
self.generator = (x for x in range(n))
def __iter__(self):
for e in self.generator:
yield e
Run Code Online (Sandbox Code Playgroud)
实际上,Foo
似乎表现得非常像xrange
:
for c in Foo(3):
print c
# 0
# 1
# 2
print list(Foo(3))
# [0, 1, 2]
Run Code Online (Sandbox Code Playgroud)
现在,子类Bar
中Foo
仅增加了一个__len__
方法:
class Bar(Foo):
def __len__(self):
return sum(1 for _ in self.generator)
Run Code Online (Sandbox Code Playgroud)
Bar
行为就像Foo
在for
-loop中使用时一样:
for c in Bar(3):
print c
# 0
# 1
# 2
Run Code Online (Sandbox Code Playgroud)
但:
print list(Bar(3))
# []
Run Code Online (Sandbox Code Playgroud)
我的猜测是,在评估中list(Bar(3))
,__len__
方法Bar(3)
被调用,从而耗尽了发生器.
(如果这个猜测是正确的,那么调用Bar(3).__len__
是不必要的;毕竟,list(Foo(3))
即使Foo
没有__len__
方法,也会产生正确的结果.)
这种情况是烦人:有没有很好的理由list(Foo(3))
,并list(Bar(3))
产生不同的结果.
是否有可能修复Bar
(当然,没有摆脱它的__len__
方法)list(Bar(3))
返回[0, 1, 2]
?
你的问题是Foo与xrange的行为不同:xrange在你每次询问它的iter
方法时给你一个新的迭代器,而Foo总是给你一个相同的意思,这意味着一旦它耗尽了对象也是:
>>> a = Foo(3)
>>> list(a)
[0, 1, 2]
>>> list(a)
[]
>>> a = range(3)
>>> list(a)
[0, 1, 2]
>>> list(a)
[0, 1, 2]
Run Code Online (Sandbox Code Playgroud)
我可以通过向您的方法添加spys来轻松确认__len__
调用该list
方法:
class Bar(Foo):
def __len__(self):
print "LEN"
return sum(1 for _ in self.generator)
Run Code Online (Sandbox Code Playgroud)
(我加了print "ITERATOR"
在Foo.__iter__
).它产生:
>>> list(Bar(3))
LEN
ITERATOR
[]
Run Code Online (Sandbox Code Playgroud)
我只能想象两个解决方法:
我的首选之一:在每次调用返回一个新的迭代器__iter__
在Foo
水平模仿xrange
:
class Foo(object):
def __init__(self, n):
self.n = n
def __iter__(self):
print "ITERATOR"
return ( x for x in range(self.n))
class Bar(Foo):
def __len__(self):
print "LEN"
return sum(1 for _ in self.generator)
Run Code Online (Sandbox Code Playgroud)
我们得到正确的:
>>> list(Bar(3))
ITERATOR
LEN
ITERATOR
[0, 1, 2]
Run Code Online (Sandbox Code Playgroud)替代方法:将len更改为不调用迭代器并Foo
保持不变:
class Bar(Foo):
def __init__(self, n):
self.len = n
super(Bar, self).__init__(n)
def __len__(self):
print "LEN"
return self.len
Run Code Online (Sandbox Code Playgroud)
我们再来一次:
>>> list(Bar(3))
LEN
ITERATOR
[0, 1, 2]
Run Code Online (Sandbox Code Playgroud)
但是一旦第一个迭代器到达终点,Foo和Bar对象就会耗尽.
但我必须承认,我不知道你真正班级的背景......