여러 수율로 반환 된 생성기 객체가 있습니다. 이 생성기를 호출하기위한 준비는 다소 시간이 걸리는 작업입니다. 그래서 발전기를 여러 번 재사용하고 싶습니다.
y = FunctionWithYield()
for x in y: print(x)
#here must be something to reset 'y'
for x in y: print(x)
물론 콘텐츠를 간단한 목록으로 복사하는 것을 고려하고 있습니다. 발전기를 재설정하는 방법이 있습니까?
답변
다른 옵션은이 itertools.tee()
함수를 사용하여 두 번째 버전의 생성기를 만드는 것입니다.
y = FunctionWithYield()
y, y_backup = tee(y)
for x in y:
print(x)
for x in y_backup:
print(x)
이것은 원래 반복이 모든 항목을 처리하지 않을 수있는 경우 메모리 사용 관점에서 유리할 수 있습니다.
답변
발전기는 되 감을 수 없습니다. 다음과 같은 옵션이 있습니다.
-
생성기 기능을 다시 실행하여 생성을 다시 시작하십시오.
y = FunctionWithYield() for x in y: print(x) y = FunctionWithYield() for x in y: print(x)
-
생성기 결과를 메모리 나 디스크의 데이터 구조에 저장하여 다시 반복 할 수 있습니다.
y = list(FunctionWithYield()) for x in y: print(x) # can iterate again: for x in y: print(x)
옵션 1 의 단점은 값을 다시 계산한다는 것입니다. CPU 집약적이라면 두 번 계산하게됩니다. 반면에 2 의 단점은 스토리지입니다. 전체 값 목록이 메모리에 저장됩니다. 값이 너무 많으면 실용적이지 않을 수 있습니다.
그래서 당신은 고전적인 메모리 대 프로세싱 트레이드 오프를 가지고 있습니다. 값을 저장하거나 다시 계산하지 않고 생성기를 되 감는 방법을 상상할 수 없습니다.
답변
>>> def gen():
... def init():
... return 0
... i = init()
... while True:
... val = (yield i)
... if val=='restart':
... i = init()
... else:
... i += 1
>>> g = gen()
>>> g.next()
0
>>> g.next()
1
>>> g.next()
2
>>> g.next()
3
>>> g.send('restart')
0
>>> g.next()
1
>>> g.next()
2
답변
아마도 가장 간단한 해결책은 고가의 부품을 객체에 싸서 생성기에 전달하는 것입니다.
data = ExpensiveSetup()
for x in FunctionWithYield(data): pass
for x in FunctionWithYield(data): pass
이렇게하면 비싼 계산을 캐시 할 수 있습니다.
모든 결과를 동시에 RAM에 보관할 수 있으면 list()
생성기 결과를 일반 목록으로 구체화하고 이를 사용 하여 작업 할 수 있습니다.
답변
오래된 문제에 대해 다른 해결책을 제시하고 싶습니다.
class IterableAdapter:
def __init__(self, iterator_factory):
self.iterator_factory = iterator_factory
def __iter__(self):
return self.iterator_factory()
squares = IterableAdapter(lambda: (x * x for x in range(5)))
for x in squares: print(x)
for x in squares: print(x)
이와 비슷한 장점 list(iterator)
은 O(1)
공간이 복잡하고 그렇다는 것 list(iterator)
입니다 O(n)
. 단점은 반복자에만 액세스 할 수 있지만 반복자를 생성 한 함수는 액세스 할 수없는 경우이 방법을 사용할 수 없다는 것입니다. 예를 들어 다음을 수행하는 것이 합리적으로 보일 수 있지만 작동하지 않습니다.
g = (x * x for x in range(5))
squares = IterableAdapter(lambda: g)
for x in squares: print(x)
for x in squares: print(x)
답변
GrzegorzOledzki의 답변이 충분하지 않다면 send()
목표를 달성 하는 데 사용할 수 있습니다. 향상된 생성기 및 수율 표현에 대한 자세한 내용 은 PEP-0342 를 참조하십시오.
업데이트 : 참조하십시오 itertools.tee()
. 그것은 위에서 언급 한 메모리 대 프로세싱 트레이드 오프의 일부를 포함하지만 , 생성기 결과를 단지 저장하는 것보다 약간의 메모리를 절약 할 수 있다 list
; 생성기를 사용하는 방법에 따라 다릅니다.
답변
생성기가 전달 된 인수와 단계 번호에만 의존한다는 의미에서 생성자가 순수하고 결과 생성기를 다시 시작하려는 경우 다음과 같은 편리한 정렬 스 니펫이 있습니다.
import copy
def generator(i):
yield from range(i)
g = generator(10)
print(list(g))
print(list(g))
class GeneratorRestartHandler(object):
def __init__(self, gen_func, argv, kwargv):
self.gen_func = gen_func
self.argv = copy.copy(argv)
self.kwargv = copy.copy(kwargv)
self.local_copy = iter(self)
def __iter__(self):
return self.gen_func(*self.argv, **self.kwargv)
def __next__(self):
return next(self.local_copy)
def restartable(g_func: callable) -> callable:
def tmp(*argv, **kwargv):
return GeneratorRestartHandler(g_func, argv, kwargv)
return tmp
@restartable
def generator2(i):
yield from range(i)
g = generator2(10)
print(next(g))
print(list(g))
print(list(g))
print(next(g))
출력 :
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
0
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1