簡體   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