gpt4 book ai didi

python - 将生成器包装为单个 `next` 调用,而不是两个步骤( __iter__ + __next__ )

转载 作者:行者123 更新时间:2023-12-01 09:10:57 25 4
gpt4 key购买 nike

我从生成器收到了未知数量的后台处理记录。如果有更重要的工作,我就得停下来释放进程。

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.

有没有一种方法可以在不插入__iter__的情况下到达__next__?有两个步骤 - (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()

最佳答案

您的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)

关于python - 将生成器包装为单个 `next` 调用,而不是两个步骤( __iter__ + __next__ ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51657482/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com