Python Generator and Coroutine 1
Generator(生成器)
在Python中,说到generator,就不得不提iterator和iterable,下面这张图来自Iterables vs. Iterators vs. Generators,这里做个简单的说明(文章很好的解释了这三者的关系与区别)。
- Container:是一个把元素组织在一起的数据结构,可以判断元素是否包含在容器当中。(大多数)容器提供了一种能够得到他们包含的每个元素的方法,这使得这些容器是iterable(可迭代对象)。
- Iterable:iterable是任何一个可以返回iterator的对象(不限于容器)。
- Iterator:带状态的对象,当对这个对象调用
next()
时,可以产生下一个值。任何有__next__()
方法的对象都是一个iterator。Iterator就像一个lazy factory,当需要时,才产生一个值返回。
下面是一个iterator的例子,与此同时也是一个iterable,
|
|
下面着重要说的是generator,genarator是一种特殊的iterator,因此它是一个惰性求值的factory。继续上面的例子,generator能够避免编写__iter__()
和__next__()
,而以一种简洁优雅的方式写出上面的fib
iterator。
|
|
当执行fib()
时,实例化并返回了一个generator,除此之外什么代码都没有被执行,包括prev, curr = 0, 1
。islice
是一个iterator,因此fib
的代码仍然未被执行。
而list
会使用其参数,由其来构造一个list,它会对islice
对象调用next()
,进而会对f
对象调用next()
,此时才开始执行fib()
里的代码,直至yield curr
,返回curr
中的值,并暂停执行fib()
,fib()
的状态被冻结了。这个值被返回给islice
,最终被添加到list里面。然后重复上述过程,直到产生第10个元素。
generator的类型有函数generator和表达式generator,表达式generator语法类似列表生成式,
|
|
到目前为止,似乎generator相比起iterator,除了更简洁以外,没有什么特别的东西。pep-0342(A new method for generator-iterators is proposed, called send(). It takes exactly one argument, which is the value that should be “sent in” to the generator).规定了一个genarator可以产生一个值,或者在产生一个值的同时还接收一个值。
|
|
第二次的list(islice(f, 0, 10))
结果不是[89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
,因为send()
改变了curr的值。
在调用send()
前,由于还未执行到yield
处,因此必须先调用一次next()
或send(None)
。
Coroutines(协程)
与Coroutines对应的概念是Subroutine(子程序)。一个普通的函数调用是这样的,从函数的第一行执行到return
语句或exception,或者执行到函数的结尾,这样也叫做一个subroutine。但有时候又希望函数能够生成一系列的值,而不仅仅是返回一个值,此时函数不应该return(return control of execution),而是yield(transfer control temporarily and voluntarily),因为函数需要稍后继续执行。generator能够冻结函数的状态,继续执行的时候恢复。
下面是用generator来实现的一个生产者-消费者模型,
|
|