在Python 3中,通过定义__iter__
和__next__
方法同时使类成为可迭代和迭代器是标准过程。但是我很难解决这个问题。以下面的示例为例,该示例创建仅产生偶数的迭代器:
class EvenNumbers:
def __init__(self, max_):
self.max_ = max_
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 * self.n
self.n += 1
return result
raise StopIteration
instance = EvenNumbers(4)
for entry in instance:
print(entry)
据我所知(如果我做错了,请纠正我),当我创建循环时,将通过调用内部itr = iter(instance)
方法之类的__iter__
之类的东西来创建迭代器。预期这将返回一个迭代器对象(该实例是由于定义了__next__
而引起的,因此我可以返回self)。要从中获取元素,将调用next(itr)
,直到引发异常为止。
现在我的问题是:__iter__
和__next__
是否以及如何分开,以便后者功能的内容在其他地方定义?什么时候有用?我知道我必须更改__iter__
才能返回迭代器。
实现此目的的想法来自该站点(LINK),该站点未说明如何实现此目标。
答案 0 :(得分:8)
这听起来像使您混淆了迭代器和可迭代对象。可迭代对象具有__iter__
方法,该方法返回迭代器。迭代器具有__next__
方法,该方法返回其下一个值或引发StopIteration
。现在在python中,stated的迭代器也是可迭代的(但反之亦然),并且iter(iterator) is iterator
因此,迭代器itr
应该仅从其__iter__
返回方法。
迭代器必须具有返回迭代器对象本身的
__iter__()
方法,因此每个迭代器也是可迭代的,并且可以在接受其他可迭代的大多数地方使用
在代码中:
class MyIter:
def __iter__(self):
return self
def __next__(self):
# actual iterator logic
如果要创建自定义迭代器类,最简单的方法是从collections.abc.Iterator
继承,您可以看到上面定义了__iter__
(它也是collections.abc.Iterable
的子类) 。然后,您只需要
class MyIter(collections.abc.Iterator):
def __next__(self):
...
当然,有一种更简单的方法来制作迭代器,那就是使用生成器函数
def fib():
a = 1
b = 1
yield a
yield b
while True:
b, a = a + b, b
yield b
list(itertools.takewhile(lambda x: x < 100, fib()))
# --> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
仅供参考,这是抽象迭代器(可迭代)的(简化)代码
from abc import ABC, abstractmethod
class Iterable(ABC):
@abstractmethod
def __iter__(self):
'Returns an instance of Iterator'
pass
class Iterator(Iterable, ABC):
@abstractmethod
def __next__(self):
'Return the next item from the iterator. When exhausted, raise StopIteration'
pass
# overloads Iterable.__iter__
def __iter__(self):
return self
答案 1 :(得分:0)
即使我不完全理解@FHTMitchell的文档段落,我现在也已经掌握了这个概念。我遇到了一个有关如何分离这两种方法的示例,并希望对此进行记录。
我发现一个非常basic tutorial,可以清楚地区分可迭代对象和迭代器(这是我感到困惑的原因)。
基本上,您首先将可迭代对象定义为单独的类:
class EvenNumbers:
def __init__(self, max_):
self.max = max_
def __iter__(self):
self.n = 0
return EvenNumbersIterator(self)
__iter__
方法仅需要一个定义了__next__
方法的对象。因此,您可以这样做:
class EvenNumbersIterator:
def __init__(self, source):
self.source = source
def __next__(self):
if self.source.n <= self.source.max:
result = 2 * self.source.n
self.source.n += 1
return result
else:
raise StopIteration
这将迭代器部分与可迭代类分开。现在有意义的是,如果我在可迭代的类中定义__next__
,则必须返回对该实例本身的引用,因为它基本上一次执行2个工作。