我的课程__iter__
定义如下:
class MyIterator:
def __iter__(self):
for value in self._iterator:
if is_iterator(value):
yield from value
else:
yield value
我想next(my_iterator)
但我必须实施__next__
才能这样做。但它会将这个简单的实现改变为相当复杂的实现 - 或者实际上我不知道如何实现它而不是将__iter__
定义为生成器函数。
一般来说,如果__iter__
被实现为没有生成器可能很难完成的生成器功能,那么如果我想使用__next__
我该怎么办?
注意:显然,next(iter(my_iterator))
有效,但我不想这样做。
答案 0 :(得分:1)
如果您的类应该是迭代器,则不应将其__iter__
方法实现为生成器函数。这使得类可迭代,但不是迭代器。迭代器的__iter__
方法应该自行返回。
如果你真的希望你的类成为迭代器,请尝试这样的事情:
class MyIterator:
def __init__(self, iterator):
self._iterator = iterator
self._subiterator = None
def __iter__(self):
return self
def __next__(self):
while True:
if self._subiterator is None:
value = next(self._iterator) # may raise StopIteration
try: # could test is_iterator(value) here for LBYL style code
self._subiterator = iter(value)
except TypeError:
return value
try:
return next(self._subiterator)
except StopIteraton:
self._subiterator = None
next(self._iterator)
来电可能会引发StopIteration
,这是我故意不会抓到的。这个例外是我们完成迭代的信号,所以如果我们抓住了它,我们只需要再次提升它。
此代码使用“更容易请求宽恕而非权限”(EAFP)方法来检测迭代器中的可迭代项目。它只是尝试在每一个上调用iter
并捕获将被提升的TypeError
,如果它们不可迭代的话。如果您更喜欢坚持使用“Look Before You Leap”(LBYL)样式并使用is_iterator
(由于它检查任何类型的可迭代,而不仅仅是迭代器)进行显式测试,您可以替换内部try
:
if is_iterator(value):
return value
else:
self._subiterator = iter(value)
在我的Python代码中,我通常更喜欢EAFP样式和LBYL样式,但有些情况下哪一个可以更好。其他时候,这只是风格问题。
答案 1 :(得分:0)
正如@BrenBarm评论的那样,明显的答案是通过保留next(iter(self))
来返回next(self._iter_self)
或self._iter_self = iter(self)
。我无法想出来。
答案 2 :(得分:0)
__next__
方法的对象,该方法返回值,直到最终提升StopIteration
。__iter__
方法的对象,它返回一个迭代器。在您的示例中,__iter__
具有收益率,因此它是一个生成器。这意味着它返回另一个迭代,而不是迭代器。这就是为什么你必须做那个奇怪的next(iter(my_iterator))
事情,而这不起作用,因为它每次都重新启动enumation。
如何最好地解决此问题取决于您如何使用此类。您可以改为创建生成器函数,然后根据需要使用iter
生成迭代器:
import collections.abc
def is_iterator(i):
return isinstance(i, collections.abc.Iterable)
def MyIterator(iterable):
for value in iterable:
if is_iterator(value):
yield from value
else:
yield value
test_this = [1,2, [3, 4, 5], [6], [], 7, 'foo']
my_iterator = iter(MyIterator(test_this))
try:
while True:
print(next(my_iterator))
except StopIteration:
pass
或者您可以实施__next__
而不是__iter__
。但是你不能使用yield
并且必须在每次调用时返回一个值,直到外部迭代器完成并引发StopIteration
。
import collections.abc
class MyIterator:
def __init__(self, iterable):
self._iterator = iter(iterable)
self._iterating = None
def __next__(self):
while True:
if self._iterating is not None:
try:
return next(self._iterating)
except StopIteration:
self._iterating = None
value = next(self._iterator)
if isinstance(value, collections.abc.Iterable):
self._iterating = iter(value)
else:
return value
test_this = [1,2, [3, 4, 5], [6], [], 7, 'foo']
my_iterator = MyIterator(test_this)
try:
while True:
print(next(my_iterator))
except StopIteration:
pass