python中yield的分析

yield是Python中比较有意思,也比较有难度,我也是阅读代码的过程中发现了这个函数的好处,yield是生成的意思,但是在python中则是作为生成器理解,生成器的用处主要可以迭代,这样简化了很多运算模型。

关于yield可以参看《Python 深入理解yieldhttp://www.jb51.net/article/15717.htm》这篇文章中比较详细的描述了yield的基本原理和问题。

yield在python2.5以后不再是一个句子,而是一个表达式,表达式是有返回值的,就如同我们在C语言中的if(expression)一样。

当函数中存在yield以后,那么该函数就不在是普通的函数了,而是一个生成器。当该函数被调用时,并不会自动执行,而是暂停中,可以从下面的代码中得到体现。

>>> def mygenerator():

... print 'start....'

... yield 5

...

>>> mygenerator()

<generator object mygenerator at 0xa2084b4>

从上面的结果可以知道,在mygenerator()之后,并没有打印出starting....说明存在yield的函数被调用的时候是没有运行的,可以认为是暂停的状态,因此需要重新启动程序。这时采用next()即可实现函数的启动。

>>> g = mygenerator()

>>> g.next()

start....

5

>>> g.next()

Traceback (most recent call last):

  File "", line 1, in <module>

StopIteration

mygenerator()中创建了对象g,这时需要next()启动程序的运行,出现了上面的结果,也就是完成了程序的重新运行,但是遇到yield后就会再次停止,从上面的效果我们可以知道,在第一次运行g.next()之后,打印,然后遇到了yield 5,这时候就会停止,再次g.next()之后,后面没有yield,说明迭代过程结束,抛出了StopIteration异常。

>>> def mygenerator2():

... print 'starting ....'

... yield 5

... print 'middle ....'

... yield 12

... print 'end ....'

...

>>> g1 = mygenerator2()

>>> g1.next()

starting ....

5

>>> g1.next()

middle ....

12

>>> g1.next()

end ....

Traceback (most recent call last):

  File "", line 1, in <module>

StopIteration

上面的代码也说明我的分析是正确的,第一次调用next()就执行到yield 5,暂停,然后next(),重新启动,并执行到yield 12,然后再次next()之后就会抛出错误,从上面打印出来的字符串就可知道。但输出中我们还发现了5,12等这是为什么呢?我认为这实质上是yield 5的返回值,因为表达式也会存在值的问题,这是肯定会输出表达式的值。

那么这是否可以认为yield 5的返回值一定是5吗?实际上并不是这样,这个与send函数存在一定的关系,这个函数实质上与next()是相似的,区别是send是传递yield表达式的值进去,而next不能传递特定的值,只能传递None进去,因此可以认为g.next()和g.send(None)是相同的。

>>> def mygenerator2():

... print 'starting ....'

... m = yield 5

... print m

... print 'middle ....'

... d = yield 12

... print d

... print 'end ....'

...

>>> g1 = mygenerator2()

>>> g1.next()

starting ....

5

>>> g1.send('pass expression')

pass expression

middle ....

12

>>> g1.next()

None

end ....

Traceback (most recent call last):

  File "", line 1, in <module>

StopIteration

>>> g1 = mygenerator2()

>>> g1.next()

starting ....

5

>>> g1.send('pass expression')

pass expression

middle ....

12

>>> g1.send('pass expression')

pass expression

end ....

Traceback (most recent call last):

  File "", line 1, in <module>

StopIteration

从上面的效果可知道,可以知道,next()的传递的实质上就是None,而send传递进来的pass expression也作为表达式yield n的返回值,因此我们可以知道,yield的返回值并不是n的值,而是和send()传递进去的参数密切相关。实质上yield n表达式的返回值是指上就是send()函数传递进去的参数,了解到这一点是非常重要的。

关于send()和next()函数的返回值也是需要我们详细去分析的,从下面的代码可以知道:

>>> def returntest():

... m = yield 5

... print m

... n = yield 10

... print n

... h = yield 15

... print h

...

>>> t = returntest()

>>> n = t.next()

>>> n

5

>>> n = t.next()

None

>>> n

10

>>> n = t.next()

None

>>> n

15

>>> t = returntest()

>>> n = t.next()

>>> n

5

>>> n = t.send(40)

40

>>> n

10

>>> n = t.send(50)

50

>>> n

15

从上面的代码我们可以知道next()和send()的返回值是与yield n的n密切相关,实际上就是yield n的n值。而yield的返回值就是send()传递进来的非None参数。

有一段经典的代码是不得不去分析的:

>>> def addlist(alist):

... for i in alist:

... yield i + 1

...

这段代码中实际上就是运用了上面几个函数的返回值,因为for ...in句式实质上就是一个迭代器,也就是说alist[i] = addlist.next(),采用next的返回值也就是yield n的参数值n,也就是完成了alist[i] = i+1.这就是返回值的灵活运用,这段代码被灵活的运用于Python的迭代器中。

总结:

yield 的返回值是send(参数)中的参数,比如send(10),那么yield的返回值就是10

send(None)和next()的意思是一样的,但是因为send不能传递None量,因此这才突显出了next()函数的作用,这两个函数的返回值通常就是yield n中的参数n。

存在yield的函数不是普通的函数,而是生成器,只有调用send()或者next()函数以后才能执行,否则就是暂停的形式,这与其他函数是存在差别的。

猜你喜欢

转载自q928856957.iteye.com/blog/2246497