Python中在一个循环中使用yield和StopIteration?
我有一个生成器,我想在实际内容的前面和后面加上一个初始值和一个结束值,类似于这样:
# any generic queue where i would like to get something from
q = Queue()
def gen( header='something', footer='anything' ):
# initial value header
yield header
for c in count():
# get from the queue
i = q.get()
# if we don't have any more data from the queue, spit out the footer and stop
if i == None:
yield footer
raise StopIteration
else:
yield i
当然,上面的代码是不能工作的——我的问题是,当队列里没有东西的时候,我希望生成器能输出一个footer
,然后再抛出一个StopIterator
。有没有什么好主意?
谢谢,
3 个回答
4
我来自未来。我推荐使用 yield from
:
def gen(q, header="header", footer="footer"):
yield header
yield from q
yield footer
输出结果:
>>> g = gen([1, 2, 3, 4])
>>> print(*g, sep="\n")
header
1
2
3
4
footer
7
这里有段代码,使用了 break 就够了,不需要用到 StopIteration:
li = [12,51,98,4,36,99,33,1,125,78,9,369,48,47,214,4]
def gen( cont, header='something', footer='anything' ):
yield header
for x in cont:
if x<100:
yield x
else:
yield footer
break
for y in gen(li):
print '1 or 2 digits only:',y
结果
1 or 2 digits only: something
1 or 2 digits only: 12
1 or 2 digits only: 51
1 or 2 digits only: 98
1 or 2 digits only: 4
1 or 2 digits only: 36
1 or 2 digits only: 99
1 or 2 digits only: 33
1 or 2 digits only: 1
1 or 2 digits only: anything
现在,这里有段稍微复杂一点的代码,我觉得在这种情况下我们是离不开 StopIteration 的。你觉得有意思吗?
import Queue
q = Queue.Queue()
li = [12,51,98,4,36,99,33,1,125,78,9,369,48,47,214,4]
def gen( cont, header='something', footer='anything' ):
def qput(ili = [0]):
eli = li[ili[0]]
q.put(eli)
ili[0] = ili[0] + 1
return eli
qput()
qput()
qput()
qput()
qput()
yield header
while True:
try:
print '\nq.qsize() first is %s' % q.qsize()
el = q.get(None)
if el>9:
print 'el==',el
yield 1000+el
qput()
else:
print 'el==%s el//3==%s' % (el,el//3)
print 'there are %s items in q and q is emptied %s times :' % (q.qsize(),el//3)
for emp in xrange(el//3):
print '%s is removed from q' % q.get(None)
if q.qsize()==0 and emp<el//3:
print 'ah !! q is now completely empty, no more emptying is possible !'
print 'q.qsize() second is %s' % q.qsize()
except Queue.Empty:
yield footer
raise StopIteration
print 'li == %s\n' % li
for i,nb in enumerate(gen(li)):
print ' * obtained from enumerate(gen(li)) : %s - %s' % (i,nb)
结果
li == [12, 51, 98, 4, 36, 99, 33, 1, 125, 78, 9, 369, 48, 47, 214, 4]
* obtained from enumerate(gen(li)) : 0 - something
q.qsize() first is 5
el== 12
* obtained from enumerate(gen(li)) : 1 - 1012
q.qsize() second is 5
q.qsize() first is 5
el== 51
* obtained from enumerate(gen(li)) : 2 - 1051
q.qsize() second is 5
q.qsize() first is 5
el== 98
* obtained from enumerate(gen(li)) : 3 - 1098
q.qsize() second is 5
q.qsize() first is 5
el==4 el//3==1
there are 4 items in q and q is emptied 1 times :
36 is removed from q
q.qsize() second is 3
q.qsize() first is 3
el== 99
* obtained from enumerate(gen(li)) : 4 - 1099
q.qsize() second is 3
q.qsize() first is 3
el== 33
* obtained from enumerate(gen(li)) : 5 - 1033
q.qsize() second is 3
q.qsize() first is 3
el==1 el//3==0
there are 2 items in q and q is emptied 0 times :
q.qsize() second is 2
q.qsize() first is 2
el== 125
* obtained from enumerate(gen(li)) : 6 - 1125
q.qsize() second is 2
q.qsize() first is 2
el== 78
* obtained from enumerate(gen(li)) : 7 - 1078
q.qsize() second is 2
q.qsize() first is 2
el==9 el//3==3
there are 1 items in q and q is emptied 3 times :
369 is removed from q
ah !! q is now completely empty, no more emptying is possible !
* obtained from enumerate(gen(li)) : 8 - anything
请注意,这个程序只有在使用 q.get(None)
时才能正确运行,而 q.get()
是不行的。
50
你似乎把这个问题搞得有点复杂了:
>>> q = [1, 2, 3, 4]
>>> def gen(header='something', footer='anything'):
yield header
for thing in q:
yield thing
yield footer
>>> for tmp in gen():
print(tmp)
something
1
2
3
4
anything
StopIteration
会在生成器停止输出时自动触发。这是生成器工作的一部分规则。除非你在做一些非常复杂的事情,否则你根本不需要(也不应该)去处理 StopIteration
。只需要依次用 yield
返回你想从生成器中输出的每个值,然后让函数自然结束就可以了。