Python迭代器和生成器

1330 0

Iterator(迭代器)

迭代器是一个带状态的对象,它能在你调用next()方法时返回容器中的下一个值,任何实现了__iter__()__next__()(Python2.x中实现next())方法的对象都是迭代器,__iter__() 返回迭代器自身,__next__()返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常。

迭代器与列表的区别在于,构建迭代器的时候,不像列表把所有元素一次性加载到内存,而是以一种延迟计算(lazy evaluation)方式返回元素,这正是它的优点。比如列表中含有一千万个整数,需要占超过100M的内存,而迭代器只需要几十个字节的空间。因为它并没有把所有元素装载到内存中,而是等到调用next()方法的时候才返回该元素(按需调用 call by need 的方式,本质上 for 循环就是不断地调用迭代器的next()方法)。

itertools模块里的函数返回的都是迭代器对象。为了更直观的感受迭代器内部的执行过程,我们自定义一个迭代器,以斐波那契数列为例:

#-*- coding:utf8 -*-
class Fib(object):
    def __init__(self, max=0):
        super(Fib, self).__init__()
        self.prev = 0
        self.curr = 1
        self.max = max

    def __iter__(self):
        return self

    def __next__(self):
        if self.max > 0:
            self.max -= 1
            # 当前要返回的元素的值
            value = self.curr
            # 下一个要返回的元素的值
            self.curr += self.prev
            # 设置下一个元素的上一个元素的值
            self.prev = value
            return value
        else:
            raise StopIteration

    # 兼容Python2.x
    def next(self):
        return self.__next__()

if __name__ == '__main__':
    fib = Fib(10)
    # 调用next()的过程
    for n in fib:
        print(n)
    # raise StopIteration
    print(next(fib))

斐波那契数列指的是 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ... 这样一个数列,这个数列从第三项开始,每一项都等于前两项之和。

Generator(生成器)

了解了迭代器之后,我们来看下生成器,普通函数用return返回一个值,还有一种函数用yield返回值,这种函数叫生成器函数。函数被调用时会返回一个生成器对象。生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅,它不需要像普通迭代器一样实现__iter__()__next__()方法了,只需要一个yield关键字。生成器一定是迭代器(反之不成立),因此任何生成器也是一种懒加载的模式生成值。下面来用生成器来实现斐波那契数列的例子:

#-*- coding:utf8 -*-
def fib(max):
    prev, curr = 0, 1
    while max > 0:
        max -= 1
        yield curr
        prev, curr = curr, prev + curr

if __name__ == '__main__':
    fib = fib(6)
    # 调用next()的过程
    for n in fib:
        print(n)
    # raise StopIteration
    print(next(fib))

上面是生成器函数,再来看下生成器的表达式,生成器表达式是列表推导式的生成器版本,看起来像列表推导式,但是它返回的是一个生成器对象而不是列表对象。

>>> x = (x*x for x in range(10))
>>> type(x)
<class 'generator'>
>>> y = [x*x for x in range(10)]
>>> type(y)
<class 'list'>

我们再看看一个例子:

#-*- coding:utf8 -*-
import time

def func(n):
    for i in range(0, n):
        # yield相当于return,下一次循环从yield的下一行开始
        arg = yield i
        print('func', arg)

if __name__ == '__main__':
    f = func(6)
    while True:
        print('main-next:', next(f))
        print('main-send:', f.send(100))
        time.sleep(1)

运行结果为:

main-next: 0
func 100
main-send: 1
func None
main-next: 2
func 100
main-send: 3
func None
main-next: 4
func 100
main-send: 5
func None
Traceback (most recent call last):
  File "demo.py", line 13, in <module>
    print('main-next:', next(f))
StopIteration

yield就是return返回一个值,并且记住这个返回的位置,下次迭代就从这个位置后(下一行)开始。next方法和send方法都可以返回下一个元素,区别在于send可以传递参数给yield表达式,这时传递的参数会作为yield表达式的值,而yield的参数是返回给调用者的值。

总结 可迭代对象(Iterable)是实现了__iter__()方法的对象,通过调用iter()方法可以获得一个迭代器(Iterator)。 迭代器(Iterator)是实现了__iter__()方法和__next()__方法的对象。 for...in...的迭代实际是将可迭代对象转换成迭代器,再重复调用next()方法实现的。 生成器(Generator)是一个特殊的迭代器,它的实现更简单优雅。 yield是生成器实现__next__()方法的关键。它作为生成器执行的暂停恢复点,可以对yield表达式进行赋值,也可以将yield表达式的值返回。

思考总结 Python

TOPIC

Python

Python知识


Comments 0