Iterator

Iterator(迭代器) 是一种常见的对象,它允许调用者方便的遍历该对象中的元素,广泛的运用于多种编程语言,如 Python, Java 和 Ruby 等。以 Python 为例,Iterator 需要实现两种方法:

  • __iter__(): 返回一个迭代器对象。

  • next(): 返回对象中的下一个元素,如果没有,则抛出 StopIteration 异常(Python 3 为 __next__())。

Java 还要求实现 hasNext() 方法:

  • hasNext():判断对象是否有下一个元素,如果有,返回 True,否则返回 False。

例如,采用 Count 构造的对象就是一个迭代器,它实现了 __iter__() 和 next() 这两个方法。

class Count(object):

    def __init__(self):
        self.count = 0

    def __iter__(self):
        return self

    def next(self):
        self.count = self.count + 1
        return self.count

运行结果:

>>> a = Count()
>>> dir(a)
[..., '__init__', '__iter__', 'count', 'next']
>>> a.next()
1
>>> a.next()
2
>>> b = a.__iter__()
>>> b.next()
3

Generator

在介绍 Generator(生成器) 前,首先需要明确一点:

  • 所有的 Generator 都是 Iterator!

所以 Generator 也必须实现 __iter__() 和 next() 这两种方法。那么 Generator 到底是什么呢?Generator 就是某个函数的返回值,但是这个函数必须包含一个或者多个 Yield 关键字。例如:

def count():
    c = 0
    while True:
        c = c + 1
        yield c

运行结果:

>>> a = count()
>>> a.next()
1
>>> a.next()
2
>>> b = a.__iter__()
>>> b.next()
3

Yield

Yield 是 Python 的关键字之一,于 Python 2.5 版本引入。通过 yield 表达式,用户可以快速的实现一个迭代器,无需自己实现 __iter__() 和 next() 等方法。当一个函数包含一个或者多个 yield 时,如果调用该函数,则返回一个生成器对象。当该对象第一次调用 next 方法时,生成器才开始执行函数直到遇到 yield 暂停挂起并保留此时状态,并把 yield 后面的参数作(如果 yield 后面没有参数,返回值为 None)为返回值返回给调用者。之后每次调用 next() 方法,生成器从上次挂起出执行,直到遇上下一个 yield,然后挂起并返回 yield 后面的参数。当调用 next() 方法至函数结束也没有遇上 yield 时,此次调用跑出 StopIteration 异常。

def func():
    yield 1
    yield 2
    yield 3

运行结果:

>>> a = func()
>>> a.next()
1
>>> a.next()
2
>>> a.next()
3
>>> a.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> dir(a)
[..., '__iter__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']

除 next() 方法外,Generator 还实现了 close(), throw(), send() 等方法。

  • close(): 结束迭代,generator 调用 close() 后如再调用 next() 会报错。
>>> a = func()
>>> a.next()
1
>>> a.close()
>>> a.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
  • throw(): Generator 结束迭代并抛出一个异常。
>>> a = func()
>>> a.next()
1
>>> a.throw(Exception)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "t.py", line 2, in func
    yield 1
Exception
>>> a.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
  • send():该方法稍微难以理解,介绍该方法前,首先看看如下代码,该代码表明 yield 不仅能返回结果,还能接收参数
def foo():
    while True:
        in_value = (yield "Please enter something")
        print in_value

运行结果如下:

>>> a = t.foo()
>>> a.next()
'Please enter something:'
>>> a.send("hello")
hello
'Please enter something:'
>>> a.send("yield")
yield
'Please enter something:'

注意,当初始化一个生成器对象时,首先需调用 next() 直到遇上 yield,然后调用 send() 方法传入参数,之后生成器继续执行直到遇到下一个 yield。另外 send(None) 和 next() 的功能完全一样,因此从功能的角度上看,next() 相当于 send() 的一个子集。

Yield 不仅可以方便的实现一个 Generator/Iterator,它还提供了 send(), next(), close() 等方法,这些方法允许调用者和 Generator 之间进行强大的通信,每次调用 next() 或者 send(),Genertor 在下一个 yield 处暂停挂起,保存状态!