繁体   English   中英

递归以重置python中的生成器

[英]recursion to reset a generator in python

我正在尝试编写一个函数,该函数返回生成器的下一个元素,如果它在生成器的末尾,它将对其进行重置并返回下一个结果。 以下代码的预期输出为:

1
2
3
1
2

但是,这显然不是我得到的。 我在做什么,那是不正确的?

a = '123'

def convert_to_generator(iterable):
    return (x for x in iterable)

ag = convert_to_generator(a)

def get_next_item(gen, original):
    try:
        return next(gen)
    except StopIteration:
        gen = convert_to_generator(original)
        get_next_item(gen, original)

for n in range(5):
        print(get_next_item(ag,a))

 1
 2
 3
 None
 None

您需要返回递归调用的结果:

return get_next_item(gen, original)

仍然没有使它可行。 for循环中使用的生成器ag不会因函数中局部变量gen的重新绑定而改变。 它将筋疲力尽...

正如评论中提到的,请查看itertools.cycle

get_next_item是一个生成器,它返回一个迭代器,该迭代器通过__next__方法为您提供它yield的值。 因此,您的陈述没有任何作用。

您要做的是:

def get_next_item(gen, original):
    try:
        return next(gen)
    except StopIteration:
        gen = convert_to_generator(original)
        for i in get_next_item(gen, original):
            return i

或更短,并且完全等效(只要gen可能具有__iter__方法):

def get_next_item(gen, original):
    for i in gen:
        yield i
    for i in get_next_item(convert_to_generator(original)):
        yield i

或不进行递归(这在python中是一个大问题,因为它的限制是1.深度有限且2.缓慢):

def get_next_item(gen, original):
    for i in gen:
        yield i
    while True:
        for i in convert_to_generator(original):
            yield i

如果convert_to_generator只是对iter的调用,它会更短:

def get_next_item(gen, original):
     for i in gen:
         yield i
     while True:
         for i in original:
             yield i

或者,使用itertools

import itertools

def get_next_item(gen, original):
    return itertools.chain(gen, itertools.cycle(original))

如果保证gen可以作为original的迭代器,则get_next_item等同于itertools.cycle。

旁注:您可以for i in x: yield i交换for i in x: yield i表示使用Python 3.3或更高版本yield from x (其中x是某些表达式) yield from x

itertools.cycle(iterable)是否可能替代?

最简单的方法是使用itertools.cycle ,否则,如果说iterable是一个迭代器(又称生成器),则需要记住iterable中的元素,以防无法重置,如果它不是迭代器,则可以重用它多次。

该文档包括一个示例实现

def cycle(iterable):
    # cycle('ABCD') --> A B C D A B C D A B C D ...
    saved = []
    for element in iterable:
        yield element
        saved.append(element)
    while saved:
        for element in saved:
            yield element

或者例如做重复使用的事情

def cycle(iterable):
    # cycle('ABCD') --> A B C D A B C D A B C D ...
    if iter(iterable) is iter(iterable): # is a iterator
        saved = []
        for element in iterable:
            yield element
            saved.append(element)
    else:
        saved = iterable
    while saved:
        for element in saved:
            yield element

示例使用

test = cycle("123")
for i in range(5):
    print(next(test))    

现在关于您的代码,问题很简单,它不记得它的状态

def get_next_item(gen, original):
    try:
        return next(gen)
    except StopIteration:
        gen = convert_to_generator(original)  # <-- the problem is here
        get_next_item(gen, original)          #and you should return something here

在标记的行中,构建了一个新的生成器,但是您需要在此函数之外更新ag变量以获得所需的行为,有多种方法可以完成此操作,例如更改函数以返回元素和生成器,还有其他方法方式,但不建议使用它们,或更不建议像构建类那样复杂,这样它就可以记住其状态

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM