生成器generator
生成器可以简单有效的创建庞大的可迭代对象,而不需要在直接在内存中创建存储整个序列
可以使用生成器推导式或者生成器函数来创建生成器
生成器函数返回数据时使用yield语句,而不是使用return
>>> def countdown(n):
... print("Counting down from %d" %n)
... while n>0:
... yield n
... n -=1
... return 'done'
...
>>> c = countdown(10) #c为一个生成器对象
可以通过next()函数或者生成器对象的__next__()方法来获得生成器的下一个返回值
当计算到最后一个元素后,没有更多的元素时,抛出StopInteration,生成器终止
>>> c = countdown(3)
>>> next(c)
Counting down from 3
3
>>> c.__next__()
2
>>> next(c)
1
>>> next(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: done
通常直接通过for循环来迭代生成器,并且不需要关心StopInteration
但是用for循环调用生成器时,发现拿不到生成器的return语句的返回值。
如果想要拿到返回值,必须捕获StopInteration错误,返回值包含在StopInteration的value中
>>> while True:
... try:
... x = next(c)
... print(c)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
当一个生成器没有全部迭代完成,但是不在使用了,可以调用close()方法关闭生成器,通常不必手动调用close()方法
c = countdown(10)
>>> next(c)
1
>>> c.close()
>>> next(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
协程
协程可以理解为就像生成器那样可以暂停的函数在函数内,当yield语句作为表达式使用,出现在赋值运算符的右边,在向函数发送值时函数将执行
这种生成器函数可以被称为协程函数,而对协程函数的调用可以获得协程对象
"协程"一词可以用于协程函数和协程对象
>>> def coroutine():
... print("Start Coroutine")
... r = 'ok'
... while True:
... n = yield r
... print('receiver %s' % n)
...
>>> c = coroutine()
>>> c.send(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator
>>> c.send(None) #next(c)
Start Coroutine
'ok'
>>> c.send(1)
receiver 1
'ok'
>>> c.close()
>>> c.send(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
在发送数据之前,应该调用c.send(None)启动生成器或者使用next()函数,使协程执行第一个yield之前的语句
启动后协程会挂起,等待协程对象c的send()方法发送值
yield语句会接收send()发送的值,并返回yield 语句后的值
协程一般会不断执行下去,可以通过close()方法关闭协程
我们可以使用asyncio模块中的@asyncio.coroutine装饰器来使协程函数在调用时自动运行到yield语句前,而不需要提前调用send(None)或next()函数
具体的asyncio模块和协程的讲解可以看另一篇文章