现在我计划在python中了解有关yield
的更多信息。我找到了一些关于yield的代码,它实现了算法Reservoir Sampling
如下:
def RandomSelect(knum, rand=None):
''' (int, func) -> list
Reservoir Sampling implementation
'''
selection = None
k_elems_list = []
count = 0
if rand is None:
rand = Random()
while True:
item = yield selection
if len(k_elems_list) < knum:
k_elems_list.append(item)
else:
# Randomly replace elements in the reservoir with a decreasing probability
r = rand.randint(0, count)
if r < knum:
k_elems_list[r] = item
count += 1
print k_elems_list
为了打破while
循环,我只需在item = yield selection
之后添加一些代码
if item == -1: # reach to the end of data, just break
break
问题1 ,有没有更好的方法来打破while循环?
调用函数RandomSelect
,
myList = [1,2,3,4,5,6,7,8,-1]
cr = RandomSelect(3);
cr.next() # advance to the yield statement, otherwise I can't call send
try:
for val in myList:
cr.send(val)
except StopIteration:
pass
finally:
del cr
我必须明确地捕获StopIteration
异常。
问题2 ,有没有更好的方法来吞下代码中的StopIteration
?
答案 0 :(得分:3)
我认为一种稍微更简洁的方法来完成正在完成的工作 - 它解决了你的两个问题 - 将通过调用它的close()
方法来终止它并突破循环来显式关闭生成器。这样做也意味着StopIteration
不需要吞下&#34;。另一个好处是,不再需要在列表末尾添加-1
哨兵值。
def RandomSelect(knum, rand=None):
''' (int, func) -> list
Reservoir Sampling implementation
'''
selection = None
k_elems_list = []
count = 0
if rand is None:
rand = Random()
while True:
try:
item = yield selection
except GeneratorExit:
break
if len(k_elems_list) < knum:
k_elems_list.append(item)
else:
# Randomly replace elements in the reservoir with a decreasing probability
r = rand.randint(0, count)
if r < knum:
k_elems_list[r] = item
count += 1
print k_elems_list
myList = [1,2,3,4,5,6,7,8]
cr = RandomSelect(3)
cr.next() # advance to the yield statement, otherwise I can't call send
for val in myList:
cr.send(val)
cr.close()
del cr
一个小小的额外增强功能(关于你没有问过的内容)就是这样做,因此在调用yield
之前手动前进到send()
语句是不必要的。 。实现这一目标的一个好方法是使用类似于名为consumer()
David Beazley在他的 Generator Tricks中描述的装饰函数
适用于PyCon 2008的系统程序员 presentation:
def coroutine(func):
""" Decorator that takes care of starting a coroutine automatically. """
def start(*args, **kwargs):
cr = func(*args, **kwargs)
cr.next()
return cr
return start
@coroutine
def RandomSelect(knum, rand=None):
.
.
.
print k_elems_list
myList = [1,2,3,4,5,6,7,8]
cr = RandomSelect(3)
#cr.next() # NO LONGER NECESSARY
for val in myList:
cr.send(val)
cr.close()
del cr