yield / generator

The yield expression is only used when defining a generator function,and can only be used in the body of a function definition. Usingayieldexpression in a function definition is sufficient to cause that definition to create a generator function instead of a normal function.我感觉generator function之所以可以称为类似于可以应用与for循环的对象,关键就是yield的这个特殊机制,到达一个地方,然后保留信息让你继续从这个地方执行。比如形式while True: yield a[i]。你给for一个a[i],然后处理完for中的语句,你再给人家一个a[i+1],然后再让它处理,这个就相当与一个迭代处理了。它随着时间产生了一个值的序列,所以就相当于实现了迭代


A generator function is a function that can be used by the for statement as if it were an iterator. A generator looks like a conventional function, with one important difference: a generator includes the yield statement.


When a generator function is called, it returns an iterator known as a generator. That generator then controls the execution of a generator function.The execution starts when one of the generator’s methods is called. At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of expression_list to generator’s caller. By suspended we mean that all local state is retained,including the current bindings of local variables, the instruction pointer, and the internal evaluation stack. When the execution is resumed by calling one of the generator’s methods, the function can proceed exactly as if the yield expression was just another external call. The value of the yield expression after resuming depends on the method which resumed the execution.

All of this makes generator functions quite similar to coroutines; they yield multiple times, they have more than one entry point and their execution can be suspended. The only difference is that a generator function cannot control where should the execution continue after it yields; the control is always transferred to the generator’s caller.

Note that calling any of the generator methods below when the generator is already executing raises a ValueError exception.

generator.next()
    Starts the execution of a generator function or resumes it at the last executed yield expression. When a generator function is resumed with a next() method, the current yield expression always evaluates to None. The execution then continues to the next yield expression, where the generator is suspended again, and the value of the expression_list is returned to next()‘s caller. If the generator exits without yielding another value, a StopIteration exception is raised.

generator.send(value)
    Resumes the execution and “sends” a value into the generator function. The value argument becomes the result of the current yield expression. The send() method returns the next value yielded by the generator, or raises StopIteration if the generator exits without yielding another value. When send() is called to start the generator, it must be called with None as the argument, because there is no yield expression that could receive the value.它会返回由下一个yield后面的表达式产生的值,实际上也就是对generator function进行了一次next处理。但是当send作为对generator function调用时的第一步时,你send过去的值,必须是一个none,因为是程序刚开始,还没有yield语句来接受这个值。

generator.throw(type[, value[, traceback]])
    Raises an exception of type type at the point where generator was paused,and returns the next value yielded by the generator function. If the generator exits without yielding another value, a StopIteration exception is raised. If the generator function does not catch the passed-in exception, or raises a different exception, then that exception propagates to the caller.它会返回下一个yield的值。如果当前位置是函数最尾了,程序结束。

generator.close()
    Raises a GeneratorExit at the point where the generator function was paused. If the generator function then raises StopIteration (by exiting normally, or due to already being closed) or GeneratorExit (by not catching the exception), close returns to its caller. If the generator yields a value, a RuntimeError is raised. If the generator raises any other exception, it is propagated to the caller. close() does nothing if the generator has already exited due to an exception or normal exit.


The essential relationship between a generator function and the for statement is the following.

1:for 调用generator,然后它开始执行generator中的语句直到第一个yield, yield后面的表达式产生一个初始值。

2:for 通过调用generator.next()方法,把上一步产生的初始值赋给target variable

3: for 开始执行自身语句。

4:for开始下一次循环的时候,会再调用generator.next()方法,然后generator function会从上一次的yield开始执行。直到再在generator function中碰见一个yield语句,然后yield后的语句产生一个值,赋予for中target variable.

5: 重复执行3,4步,直到在generator function中碰见return语句或者函数结果,这两种情况最终都会报StopIteration用于指示迭代结束,StopIteration 信息只会在显示执行generator.next()才会出现,在for循环中它的出现代表这程序结束,不会显示出来。

下面是一个Python2的版本,我也试着该了一下,但是exception那儿仍旧有问题,等将来再发一个python3的版本

>>> def echo(value=None):
...     print "Execution starts when 'next()' is called for the first time."
...     try:
...         while True:
...             try:
...                 value = (yield value)
...             except Exception, e:
...                 value = e
...     finally:
...         print "Don't forget to clean up when 'close()' is called."
...
>>> generator = echo(1)
>>> print generator.next()
Execution starts when 'next()' is called for the first time.
1
>>> print generator.next()
None
>>> print generator.send(2)
2
>>> generator.throw(TypeError, "spam")
TypeError('spam',)
>>> generator.close()
Don't forget to clean up when 'close()' is called.

理解一下下面的例子,其实也就是想说for 循环的第一步就是执行iter()然后你返回给他一个iterator对象就行了。如果返回本身的话,下面要有next()方法定义;如果不是本身的话,你也可以给他一个generator function.因为我们在前面说了它也类似与一个可以用for循环迭代处理的对象。

class ByteValue(object):

	def __init__(self, data):
		self.data = data

	def __iter__(self):
		return self.forward()                  #返回一个拥有yield调用的函数,就下来就完全类似于对一个生成器函数的处理方式

	def forward(self):
		#The forward generator
		current_item = 0
		while (current_item < len(self.data)):
			byte_value = ord(self.data[current_item])
			current_item += 1
			yield byte_value

	def reverse(self):
		#The reverse generator
		current_item = len(self.data)
		while (current_item > 0):
			current_item -= 1
			yield ord(self.data[current_item])

测试用例及输出信息

>>> def main():
... 	bv=ByteValue("abc")
... 	for v in bv:
... 		if v in bv:
... 			print("we have a %d" % v)
... 
>>> 
>>> main()
we have a 97
we have a 98
we have a 99


下面的例子主要是让你注意到底那个是target variable, yield会把值赋给x 而不是i

>>> def test(list):
... 	for i in list:
... 		print(i)
... 		yield i+1
... 		print(i)
... 
>>> a=(1,6,9,15)
>>> test(a)
<generator object test at 0xb70c489c>
>>> for x in test(a):
... 	print (x)



参考文献

6.8. The yield statement

5.2.10. Yield expressions

Generator Function Semantics

Learning Python

关于python的迭代器


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值