I 주장 : Python에서 체인 생성기는 메모리가 비효율적이어서 특정 유형의 응용 프로그램에서 사용할 수 없게 만듭니다. 가능하다면 틀린 나를 증명해주십시오.연결 발전기가 유해하다고 생각합니까?
첫째, 발전기없이 매우 간단하고 직선적 예 : 예상대로
import gc
def cocktail_objects():
# find all Cocktail objects currently tracked by the garbage collector
return filter(lambda obj: isinstance(obj, Cocktail), gc.get_objects())
class Cocktail(object):
def __init__(self, ingredients):
# ingredients represents our object data, imagine some heavy arrays
self.ingredients = ingredients
def __str__(self):
return self.ingredients
def __repr__(self):
return 'Cocktail(' + str(self) + ')'
def create(first_ingredient):
return Cocktail(first_ingredient)
def with_ingredient(cocktail, ingredient):
# this could be some data transformation function
return Cocktail(cocktail.ingredients + ' and ' + ingredient)
first_ingredients = ['rum', 'vodka']
print 'using iterative style:'
for ingredient in first_ingredients:
cocktail = create(ingredient)
cocktail = with_ingredient(cocktail, 'coke')
cocktail = with_ingredient(cocktail, 'limes')
print cocktail
print cocktail_objects()
이 인쇄 :
rum and coke and limes
[Cocktail(rum and coke and limes)]
vodka and coke and limes
[Cocktail(vodka and coke and limes)]
이제
의이 칵테일 변환을 쉽게 작성 가능하게 반복자 객체를 사용하자 :
class create_iter(object):
def __init__(self, first_ingredients):
self.first_ingredients = first_ingredients
self.i = 0
def __iter__(self):
return self
def next(self):
try:
ingredient = self.first_ingredients[self.i]
except IndexError:
raise StopIteration
else:
self.i += 1
return create(ingredient)
class with_ingredient_iter(object):
def __init__(self, cocktails_iter, ingredient):
self.cocktails_iter = cocktails_iter
self.ingredient = ingredient
def __iter__(self):
return self
def next(self):
cocktail = next(self.cocktails_iter)
return with_ingredient(cocktail, self.ingredient)
print 'using iterators:'
base = create_iter(first_ingredients)
with_coke = with_ingredient_iter(base, 'coke')
with_coke_and_limes = with_ingredient_iter(with_coke, 'limes')
for cocktail in with_coke_and_limes:
print cocktail
print cocktail_objects()
출력은 이전과 동일합니다.
rum and coke and limes
[Cocktail(rum), Cocktail(rum and coke), Cocktail(rum and coke and limes)]
vodka and coke and limes
[Cocktail(vodka), Cocktail(vodka and coke), Cocktail(vodka and coke and limes)]
이 발전기의 체인의 모든 현재 개체를 굴복을 의미합니다 :
def create_gen(first_ingredients):
for ingredient in first_ingredients:
yield create(ingredient)
def with_ingredient_gen(cocktails_gen, ingredient):
for cocktail in cocktails_gen:
yield with_ingredient(cocktail, ingredient)
print 'using generators:'
base = create_gen(first_ingredients)
with_coke = with_ingredient_gen(base, 'coke')
with_coke_and_limes = with_ingredient_gen(with_coke, 'limes')
for cocktail in with_coke_and_limes:
print cocktail
print cocktail_objects()
이 그러나 인쇄 : 마지막으로
,의 보일러 플레이트 없애 발전기 반복자를 대체 할 수 이전 체인 위치의 체인이 더 이상 필요하지 않더라도 체인은 메모리에 남아 있고 해제되지 않습니다. 결과 : 필요한 메모리 소비보다 높습니다.이제 질문은 다음과 같습니다. 왜 생성기는 다음 반복이 시작될 때까지 생성하는 객체를 보유하고 있습니까? 분명히 물체는 발전기에서 더 이상 필요하지 않으며 그것들에 대한 참조는 해제 될 수 있습니다.
저는 일종의 파이프 라인에서 무거운 데이터 (수백 메가 바이트의 뭉툭한 배열)를 변환하기 위해 제 프로젝트에서 발전기를 사용하고 있습니다. 그러나 보시다시피 이것은 매우 비효율적 인 메모리입니다. 파이썬 2.7을 사용하고 있습니다. 이것이 파이썬 3에서 수정 된 동작이라면, 말해주십시오. 그렇지 않으면 버그 리포트가 필요한가? 그리고 가장 중요한 것은 표시된대로 다시 쓰기를 제외한 모든 해결 방법이 있습니까?
해결 방법 1 : 더 상태가 "수확량"사이에서 유지 될 필요가없는 경우
print 'using imap:'
from itertools import imap
base = imap(lambda ingredient: create(ingredient), first_ingredients)
with_coke = imap(lambda cocktail: with_ingredient(cocktail, 'coke'), base)
with_coke_and_limes = imap(lambda cocktail: with_ingredient(cocktail, 'limes'), with_coke)
for cocktail in with_coke_and_limes:
print cocktail
print gc.collect()
print cocktail_objects()
은 분명히이 만 가능한 것입니다. 예에서 이것은 사실입니다.
예비 결론 : 반복자 클래스를 사용하는 경우 은 유지할 상태를 결정합니다. 생성자를 사용하는 경우 파이썬은 유지할 상태를 암시 적으로 결정합니다. itertools.imap
을 사용하면 상태를 유지할 수 없습니다.
python 3의 경우 : 'yield from'은 생성기를 체인화하는보다 효율적인 방법입니다. – roippi
@roippi 이러한 메모리 효과를 방지하거나 쓰기에 더 효율적이라는 의미에서 더 효율적입니까? – letmaik
'yield from'는 AFAIK라는 질문에서 제기하는 메모리 사용 문제를 해결하지 않을 것입니다.'yield from'이 허용하는 최적화는 [해당 PEP] (http://legacy.python.org/dev/peps/pep-0380/#optimisations)에 따라 속도와 관련이 있습니다. – dano