반복 가능한 객체 (iterable object)
반복형 객체(for, collections, text file, List, Dict, Set, Tuple, unpacking, *args)
반복형 객체는 내부적으로 iter(x) 함수를 호출한다. yield from
t = 'ABCDEF'
# while 사용
w = iter(t)
while True:
try:
print(next(w))
except StopIteration: # next() 가 더 이상 없음
break
# 반복형 확인
print(hasattr(t, '__iter__')) # True
print(isinstance(t, abc.Iterable)) # True
next() 를 사용하는 순회 객체
클래스에 매직 메소드 __next__
와 __iter__
가 구현되어있기 때문에 이터러블한 객체가 된다.
class WordSplitIter:
def __init__(self, text):
self._idx = 0
self._text = text.split(' ')
def __next__(self):
# print('Called __next__')
try:
word = self._text[self._idx]
except IndexError: # 더이상 다음이없어
raise StopIteration('Stop!') # 에러가 발생.
self._idx += 1
return word
def __iter__(self):
print('Called __iter__')
return self
def __repr__(self):
return 'WordSplit(%s)' % (self._text)
wi = WordSplitIter('Who says the nights are for sleeping')
print(wi) # WordSplit(['Who', 'says', 'the', 'nights', 'are', 'for', 'sleeping'])
print(next(wi)) # Who
print(next(wi)) # says
순회 객체의 Generator 패턴 사용
yield
를 통해 __next__
의 기능을 대체한다.
지능형 리스트, 딕셔너리, 집합 -> 데이터 셋이 증가 될 경우 메모리 사용량 증가 -> 제네레이터 완화
단위 실행 가능한 코루틴(Coroutine) 구현에 아주 중요
딕셔너리, 리스트 한 번 호출 할 때 마다 하나의 값만 리턴 -> 아주 작은 메모리 양을 필요로 함
class WordSplitGenerator:
def __init__(self, text):
self._text = text.split(' ')
def __iter__(self):
for word in self._text:
yield word # 제네레이터 __next__ 함수에서 하는 역할을 다 해줌
return
def __repr__(self):
return 'WordSplit(%s)' % (self._text)
wg = WordSplitGenerator('Who says the nights are for sleeping')
wt = iter(wg)
print(wt)
# <generator object WordSplitGenerator.__iter__ at 0x000001CF7B81B848>
print(next(wt)) # who
print(next(wt)) # says
# sleeping 까지 next(wt) 가능
제너레이터를 이해를 위한 예제
# for문은 IndexError 을 체크해서 멈추기 때문에 자동으로 StopIteration 에러발생을 잡아준다.
def generator_ex1():
print('start')
yield 'AAA'
print('continue')
yield 'BBB'
print('end')
temp = iter(generator_ex1())
for v in generator_ex1():
print(v)
# start
# AAA
# continue
# BBB
# end
제너레이터에서 지원하는 함수를 이용한 예제
import itertools
# 1 부터 2.5 씩 증가하는 무한의 수
gen1 = itertools.count(1, 2.5)
print(next(gen1)) # 1
print(next(gen1)) # 3.5
print(next(gen1)) # 6.0
print(next(gen1)) # 8.5
# ... 무한
#############################
# 종료값 조건 까지, 함수식 반복
gen2 = itertools.takewhile(lambda n : n < 1000, itertools.count(1, 2.5))
for v in gen2:
print(v) # v 가 1000 미만의 최대값 까지 함수식 next 가능
#############################
# 필터 반대
gen3 = itertools.filterfalse(lambda n : n < 3, [1,2,3,4,5])
for v in gen3:
print(v) # 조건의 맞지 않는 반대결과가 next 됨
# 여기 예제에서 3, 4, 5
############################
# 누적 합계
gen4 = itertools.accumulate([x for x in range(1, 101)])
for v in gen4:
print(v) # 마치 reduce(함수, iter_obj) 처럼 누적 계산
############################
# 반복 가능한 객체의 요소를 합쳐서 하나로 만듦
gen5 = itertools.chain('ABCDE', range(1,11,2))
print(list(gen5)) # ['A', 'B', 'C', 'D', 'E', 1, 3, 5, 7, 9]
gen6 = itertools.chain(enumerate('ABCDE'))
print(list(gen6)) # [(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D'), (4, 'E')]
############################
# 개별적으로 반복 가능한 객체를
gen7 = itertools.product('ABCDE')
print(list(gen7)) # [('A',), ('B',), ('C',), ('D',), ('E',)]
# 연산(경우의 수)
gen8 = itertools.product('ABCDE', repeat=2)
print(list(gen8))
# [('A', 'A'), ('A', 'B'), ('A', 'C'), ... , ('E', 'C'), ('E', 'D'), ('E', 'E')]
############################
# 그룹화
gen9 = itertools.groupby('AAABBCCCCDDEEE')
# print(list(gen9))
# [('A', <itertools._grouper>), ... , ('E', <itertools._grouper>)]
for chr, group in gen9:
print('chr, ' : ', list(group))
# A : ['A', 'A', 'A']
# B : ['B', 'B']
# C : ['C', 'C', 'C', 'C']
# D : ['D', 'D']
# E : ['E', 'E', 'E']
# 요소의 경우의수
gen10 = itertools.permutations('ABC',3)