回顾生成器表达式
在条件和循环(包括迭代器、生成器)这篇博客里面我们简单的介绍过生成器表达式。回顾一下:
生成器表达式和列表解析式非常相似,基本语法基本相同;不过它并不真正创建列表,而是返回一个生成器。这个生成器在每次计算出一个条目后,把这个条目“yield(产生)”出来。生成器表达式使用了“延迟计算(lazy evaluation)”,所以在内存上更有效。
语法:
(expr for iter_var in iterable if cond_expr)
这里我们介绍的是生成器,它是一个带有yield表达式
的函数。
生成器和迭代器的关系
生成器是一种特殊的迭代器。
迭代器中所有值都是已经生成好的,它解决的是“在所有值都存在的条件下,如何索引”的问题。生成器提出的动机是“在迭代中以某种方式生成下一个值并且返回”。它体现了函数式编程里的延迟计算
的特点。
在使用迭代器的场景使用生成器对内存较为友好。
简单生成器
>>> def simpleGen():
yield 1
yield 'haha 2'
>>> generator = simpleGen()
>>> generator
<generator object simpleGen at 0x02ACD288>
>>> generator.next()
1
>>> generator.next()
'haha 2'
>>> generator.next()
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
generator.next()
StopIteration
分析:
- 第一次调用生成器的next方法时,生成器才开始执行生成器函数,直到遇到yield时暂停执行(挂起),并且yield的参数将作为此次next方法的返回值
- 之后每次调用生成器的next方法,生成器将从上次暂停执行的位置恢复执行生成器函数,直到再次遇到yield时暂停,并且同样的,yield的参数将作为next方法的返回值
- 如果当调用next方法时生成器函数结束(遇到空的return语句或是到达函数体末尾),则这次next方法的调用将抛出StopIteration异常
因为python的for循环
有next()函数
调用以及有对StopIteration异常
的处理,所以我们可以直接迭代生成器:
>>> for item in simpleGen():
print item
1
haha 2
>>>
增强的生成器
为什么说它增强了,因为它支持了协同程序(coroutine)。
协同程序的定义
协同程序是一个独立的函数调用,它能够运行、暂停和挂起,也能够在暂停或挂起的地方开始继续运行。
它也支持调用者和被调用者之间的交流。
使用了协同的生成器
可以使用next()
、send()
、close()
进行协同。
调用者通过send()
给被调用者发送信息;你可以随时使用close()
来关闭一个生成器。
看例子
>>> def counter(start_at=0):
count = start_at
while True:
val = (yield count) # 返回count(给next()),接收val(从send())
if val is not None:
count = val
else:
count += 1
>>> count = counter(5) # 生成器被创建
>>> count.next()
5
>>> count.next()
6
>>> count.send(9) # val变量被设置为9
9
>>> count.next()
10
>>> count.close() # 生成器被关闭
>>> count.next()
Traceback (most recent call last):
File "<pyshell#30>", line 1, in <module>
count.next()
StopIteration
协程(协同程序)的特点决定了同一时刻只能有一个协同程序正在运行(忽略多线程的情况)。得益于此,协程间可以直接传递对象而不需要考虑资源锁、或是直接唤醒其他协程而不需要主动休眠,就像是内置了锁的线程。在符合协程特点的应用场景,使用协程无疑比使用线程要更方便。
从另一方面说,协程无法并发其实也将它的应用场景限制在了一个很狭窄的范围,这个特点使得协程更多的被拿来与常规函数进行比较,而不是与线程。当然,线程比协程复杂许多,功能也更强大
Ref
http://www.cnblogs.com/huxi/archive/2011/07/14/2106863.html
《Python核心编程》