我从发生器接收到数量不明的记录用于后台处理。如果还有更重要的工作,我必须停止发布流程。
main
流程最好描述为:
def main():
generator_source = generator_for_test_data() # 1. contact server to get data.
uw = UploadWrapper(generator_source) # 2. wrap the data.
while not interrupt(): # 3. check for interrupts.
row = next(uw)
if row is None:
return
print(long_running_job(row)) # 4. do the work.
是否有一种无需插入__next__
即可到达__iter__
的方法?
有两个步骤-(1)创建一个迭代器,然后(2)对其进行迭代,看起来很笨拙。
在很多情况下,我希望将功能提交给功能管理器(mapreduce样式),但是在这种情况下,我需要带有一些设置的实例化类。因此,仅当单个功能为__next__
class UploadWrapper(object):
def __init__(self, generator):
self.generator = generator
self._iterator = None
def __iter__(self):
for page in self.generator:
yield from page.data
def __next__(self):
if self._iterator is None: # ugly bit.
self._iterator = self.__iter__() #
try:
return next(self._iterator)
except StopIteration:
return None
问:有没有更简单的方法?
添加了工作样本以提高完整性:
import time
import random
class Page(object):
def __init__(self, data):
self.data = data
def generator_for_test_data():
for t in range(10):
page = Page(data=[(t, i) for i in range(100, 110)])
yield page
def long_running_job(row):
time.sleep(random.randint(1,10)/100)
assert len(row) == 2
assert row[0] in range(10)
assert row[1] in range(100, 110)
return row
def interrupt(): # interrupt check
if random.randint(1,50) == 1:
print("INTERRUPT SIGNAL!")
return True
return False
class UploadWrapper(object):
def __init__(self, generator):
self.generator = generator
self._iterator = None
def __iter__(self):
for ft in self.generator:
yield from ft.data
def __next__(self):
if self._iterator is None:
self._iterator = self.__iter__()
try:
return next(self._iterator)
except StopIteration:
return None
def main():
gen = generator_for_test_data()
uw = UploadWrapper(gen)
while not interrupt(): # check for job interrupt.
row = next(uw)
if row is None:
return
print(long_running_job(row))
if __name__ == "__main__":
main()
答案 0 :(得分:1)
您的UploadWrapper
似乎很复杂,不仅有一个简单的解决方案。
我的第一个想法是完全放弃该类,而只使用一个函数:
def uploadwrapper(page_gen):
for page in page_gen:
yield from page.data
只需将uw = UploadWrapper(gen)
替换为uw = uploadwrapper(gen)
,就可以了。
如果您坚持上课,则可以摆脱__next__()
并将uw = UploadWrapper(gen)
替换为uw = iter(UploadWrapper(gen))
,它将起作用。
在任何一种情况下,您还必须在呼叫方中捕获StopIteration
。 __next__()
被假定完成后会提高StopIteration
,而不像您一样返回None
。否则,它将无法与期望良好行为的迭代器一起使用,例如。 for
循环。
我认为您可能对这一切应该如何融合有一些误解,因此,我将尽我所能尽力解释它应该如何运作:
__iter__()
的要点是,如果您有例如。列表中,您可以通过调用iter()
获得多个独立的迭代器。当您有一个for
循环时,您实际上首先要获得一个带有iter()
的迭代器,然后在每次循环迭代时对其调用next()
。如果您有两个使用相同列表的嵌套循环,则迭代器及其位置仍然是分开的,因此不会发生冲突。 __iter__()
应该为其所在的容器返回一个迭代器,或者如果它在迭代器上被调用,则应该仅返回self
。从这种意义上讲,UploadWrapper
不返回self
中的__iter__()
是错误的,因为它包装了一个生成器,因此实际上不能给出独立的迭代器。至于省去__next__()
的原因,是因为在定义生成器(即在函数中使用yield
)时,生成器有一个__iter__()
(返回self
,它应该会)和__next__()
达到您的期望。在您的原始代码中,您实际上并没有真正使用__iter__()
来使用它:即使您将其重命名为其他代码,代码也可以正常工作!这是因为您永远不会在实例上调用iter()
,而直接调用next()
。
如果您想作为一个班级“适当地”做,我想这样的话就足够了:
class UploadWrapper(object):
def __init__(self, generator):
self.generator = generator
self.subgen = iter(next(generator).data)
def __iter__(self):
return self
def __next__(self):
while True:
try:
return next(self.subgen)
except StopIteration:
self.subgen = iter(next(self.generator).data)