什么是yield?
yield有点类似于continue和return的结合, 代码的运行的过程方式都是一样的, 执行完一行之后运行下一行.
在continue里面(请查看注意事项里面的2说明), 当执行的代码遇到continue的时候, 就不会再执行后续的语句, 而是跳到循环的头部位置进行下一次循环.
在yield里面(请查看注意事项里面的1说明), 当执行的代码遇到yield的时候, 就不会再执行后续的语句, 而是跳出函数, 并返回结果.
yield运行原理
场景1: 最简单的yield代码样例
[root@localhost ~]# vim test_yield.py
#!/bin/env python
# -.- coding:utf-8 -.-
def hello(n):
print 'yield之前'
n += 1
yield n
print 'yield之后'
n *= 2
print n
s = hello(100)
print '运行第一个next'
s.next()
# 运行结果
[root@localhost ~]# python test_yield.py
运行第一个next
yield之前 # 这个是输出结果.
总结: 通过这段代码可以看出来, 当执行的代码遇到yield的时候, 就不会再执行后续的语句, 而是跳出函数, 并返回结果.
场景2: 在场景1代码的例子上增加一个next(), 观察输出结果
[root@localhost ~]# vim test_yield.py
#!/bin/env python
# -.- coding:utf-8 -.-
def hello(n):
print 'yield之前'
n += 1
yield n
print 'yield之后'
n *= 2
print n
s = hello(100)
print '运行第一个next'
s.next()
print '\n\n运行第二个next' # 增加了这行代码
s.next() # 增加了这行代码
# 运行结果
[root@localhost ~]# python test_yield.py
运行第一个next
yield之前
运行第二个next
yield之后
202
Traceback (most recent call last):
File "test_yield.py", line 34, in <module>
s.next()
StopIteration
总结: 通过这段代码可以看出来, 当第二次运行next的时候, 其实是先运行第一次阻断后面的内容, 然后才报错, 这个报错是因为没有next了.
场景3: 其实可以把yield理解为暂停功能.
#!/bin/env python
# -.- coding:utf-8 -.-
def get_time():
from datetime import datetime
return datetime.now()
def hello(n):
for i in xrange(n):
print 'yield之前',
print get_time()
yield n
print 'yield之后',
print get_time()
s = hello(5)
print '运行第一个next'
s.next()
print '\n\n运行第二个next'
s.next()
print '\n\n运行第三次next'
s.next()
print '\n\n运行第四次next'
s.next()
print '\n\n运行第五次next'
s.next()
print '\n\n运行第六次next'
s.next()
# 运行结果
[root@localhost ~]# python test_yield.py
运行第一个next
yield之前 2015-11-10 01:58:31.753858
运行第二个next
yield之后 2015-11-10 01:58:31.753908
yield之前 2015-11-10 01:58:31.753941
运行第三次next
yield之后 2015-11-10 01:58:31.753952
yield之前 2015-11-10 01:58:31.753957
运行第四次next
yield之后 2015-11-10 01:58:31.753965
yield之前 2015-11-10 01:58:31.753970
运行第五次next
yield之后 2015-11-10 01:58:31.753977
yield之前 2015-11-10 01:58:31.753982
运行第六次next
yield之后 2015-11-10 01:58:31.753989
Traceback (most recent call last):
File "test_yield.py", line 67, in <module>
s.next()
StopIteration
总结: 通过上面这三个代码应该就已经大概知道yield是什么东西, 也知道它是怎么运作的了, 但是它老是报StopIteration这个错误.
场景4: 利用遍历来规避StopIteration错误
[root@localhost ~]# vim test_yield.py
#!/bin/env python
# -.- coding:utf-8 -.-
def get_time():
from datetime import datetime
return datetime.now()
def hello(n):
for i in xrange(n):
print 'yield之前',
print get_time()
yield n
print 'yield之后',
print get_time()
counter = 1
for i in hello(5):
print '\n\n运行第%s个next' % counter
counter += 1
# 运行结果
[root@localhost ~]# python test_yield.py
yield之前 2015-11-10 02:03:50.148205
运行第1个next
yield之后 2015-11-10 02:03:50.148256
yield之前 2015-11-10 02:03:50.148265
运行第2个next
yield之后 2015-11-10 02:03:50.148274
yield之前 2015-11-10 02:03:50.148279
运行第3个next
yield之后 2015-11-10 02:03:50.148286
yield之前 2015-11-10 02:03:50.148291
运行第4个next
yield之后 2015-11-10 02:03:50.148299
yield之前 2015-11-10 02:03:50.148304
运行第5个next
yield之后 2015-11-10 02:03:50.148311
场景5: yield传参
[root@localhost ~]# vim multi-para_yield.py
#!/bin/env python
# -.- coding:utf-8 -.-
def hello(n):
for i in xrange(n):
print 'yield之前'
new_value = yield i
print 'yield之后'
print 'new_value is:', new_value, i
s = hello(3)
print '\n\n第一次next'
s.next()
print '\n\n第二次next'
s.next()
print '\n\n第三次next(其实是send)'
s.send('hello world!')
# 运行结果
[root@localhost ~]# python multi-para_yield.py
第一次next
yield之前
第二次next
yield之后
new_value is: None 0
yield之前
第三次next(其实是send)
yield之后
new_value is: hello world! 1 # 重点在这里, 会发现传参和自身变量截然不同, 这应该是yield传参的固有写法.
yield之前
注意事项:
1. yield只能写在函数里面, 不能写在函数外面.
2. continue只能运行在循环语句中.
3. 无法使用try来规避调用next时报的StopIteration错误.
4. yield对象所有属性:
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__',
'__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']