๐ ์ด ํฌ์คํ ์์๋ Python์ Generator์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค.
๐ฅ ๋ฐ๋ณตํ๊ณผ iter
๐ฅ Generator๋ ์ ์ธ๊น์?
๐ฅ Generator ๋ด์ฅ ํจ์
โ๏ธ for, collections, List, Dict, Set, Tuple, unpacking ๋ฑ์ ๋ฐ๋ณตํ ๋ฐ์ดํฐ ํ์ ์ด ๋ฐ๋ณต์ด ๊ฐ๋ฅํ ์ด์ ๋ ๋ด๋ถ์ ์ผ๋ก iter ํจ์๋ฅผ ๊ฐ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
# for ์ฌ์ฉ t = "ABCDEF" for i in t: print(i)
# while๋ฌธ ์ฌ์ฉ t = "ABCDEF" w = iter(t) # ๐ ๋ฐ๋ณตํ์ iter์ ์ฌ์ฉํ ์ ์์ while True: try: print(next(w)) except StopIteration: # ๐ StopIteration ์์ธ๊ฐ ๋ฐ์ํ๋ฉด, ๋ฐ๋ณต์ ์ค๋จ break
โ๏ธ __iter__
์์ฑ์ ๊ฐ์ง๊ณ ์๋์ง ํ์ธํ๋ ค๋ฉด, dir๋ก ํ์ธํ ์ ์์ต๋๋ค.
โ๏ธ hasattr ํจ์๋ ๋์์ด ํด๋น ์์ฑ์ ๊ฐ์ง๊ณ ์๋์ง ํ์ธํ์ฌ Boolean๊ฐ์ผ๋ก ๋ฐํํด์ค๋๋ค.
โ๏ธ ์ด๋ฅผ ์ด์ฉํ์ฌ ํ์ธํ ๋์๊ณผ, iter ์์ฑ์ ์ ๋ฌํ์ฌ ๋ฐ๋ณต์ด ๊ฐ๋ฅํ์ง ์์ฝ๊ฒ ํ์ธ ๊ฐ๋ฅํฉ๋๋ค.
# ๋ฐ๋ณต ๊ฐ๋ฅ์ฌ๋ถ ํ์ธ t = "ABCDEF" print(hasattr(t, '__iter__')) # True print(hasattr(12345, '__iter__')) # False
โ๏ธ ๋ฐ๋ณตํ์์ ๋ค์ ์์์ธ ์์๋ฅผ ๊ฐ๋ฅดํค๊ธฐ๋งํ๊ณ , next๋ฅผ ์คํํ ๋ ๋ฐํํด์ฃผ๋ ๊ฒ์ด Generator์ ํจํด์ ๋๋ค. Generator๋ ๊ฐ ์์๋ฅผ ๋ฐ์ ๊ธฐ์์ ์์ฐํ๋ฏ์ด ํธ์ถํ ๋๋ง ํ๋ํ๋ ๋ฐํ์์ผ์ค๋๋ค.
# Generator ํจํด class wordSplitIter: def __init__(self, text): self._idx = 0 self._text = text.split(" ") # next ๋งค์๋ ์ค๋ฒ๋ผ์ด๋ฉ def __next__(self): try: word = self._text[self._idx] # ๐ ์ธ๋ฑ์ค๋ฅผ ์ด์ฉํ์ฌ ์์๋ฅผ ์์ฐจ์ ์ผ๋ก ๊ฐ๋ฅดํต๋๋ค. except IndexError: raise StopIteration() self._idx += 1 # ๐ index๋ฅผ 1์ฉ ์ฆ๊ฐ์ํต๋๋ค. return word # ๐ ์์๋ฅผ ๋ฐํํฉ๋๋ค. def __repr__(self): return f"{self._text}" # ๊ฐ์ฒด ์์ฑ wi = wordSplitIter("What are you going to have for dinner tonight?") print(wi) # ['What', 'are', 'you', 'going', 'to', 'have', 'for', 'dinner', 'tonight?'] print(next(wi)) # What print(next(wi)) # are print(next(wi)) # you print(next(wi)) # going print(next(wi)) # to print(next(wi)) # have print(next(wi)) # for print(next(wi)) # dinner print(next(wi)) # tonight?
โ๏ธ ์์ ์ฝ๋ ์ฒ๋ผ next ๋งค์๋์ ์ค๋ฒ๋ผ์ด๋ฉํ์ฌ ๊ตฌํํ๋ฉด, next๋งค์๋ฅผ ํตํด index๋ฅผ ๊ธฐ์ตํ๊ณ ์์ด์ผํฉ๋๋ค. ์ด๋ฐ ๋ถํ์ํ ๊ฒ๋ค์ iter ๋งค์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํ๋ฉด ์๋์ฒ๋ผ ๊ฐํธํ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
โ๏ธ "yield" ์์ฝ์ด๋ ๋ฐ๋ณตํ์ index๋ฅผ ๊ธฐ์ตํ์ฌ ๋ค์ ํธ์ถ์ด ๋ฐ์ํ ๋ ๋ฐํํ ์์๋ฅผ ์ค๋น์ํค๊ณ yield๋ฅผ ๋ง๋๋ฉด ์ ์งํฉ๋๋ค.
โ๏ธ ๋ํ "yield" ์์ฝ์ด๋ ๋ชจ๋ ์ํํ์ ๋, StopIteration Error๋ฅผ ์๋์ผ๋ก ์ผ์ผํต๋๋ค. ์ด์ "StopIteration"์ except ๊ตฌ๋ฌธ์ ์ง์ ์์ฑํ์ง ์์๋ ๋ฉ๋๋ค.
# Generator ๊ตฌํ class wordSplitGenerator: def __init__(self, text): self._text = text.split(" ") def __iter__(self): for word in self._text: yield word # ๐ ์ด๊ฒ์ด ๋ฐ๋ก Generator์ ๋๋คใ . return def __repr__(self): return f"{self._text}" wg = wordSplitGenerator("What are you going to have for dinner tonight?") wt = iter(wg) # ๐ iter์ ์ ๋ฌํด์ค๋ค ์ฌ์ฉํฉ๋๋ค. print(wt) # <generator object wordSplitGenerator.__iter__ at 0x7ff3ef401900> print(next(wt)) # What print(next(wt)) # are print(next(wt)) # you print(next(wt)) # going print(next(wt)) # to print(next(wt)) # have print(next(wt)) # for print(next(wt)) # dinner print(next(wt)) # tonight?
โ๏ธ Generator ํจํด์ ์ฌ์ฉํ๋ฉด ์ง๋ฅํ ๋ฆฌ์คํธ, ๋์
๋๋ฆฌ, ์งํฉ ๋ฑ์ ๋ฐ๋ณตํ์ ์ฌ์ฉํ ๋ ๋ณด๋ค ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๊ฐ์์ํฌ ์ ์์ต๋๋ค.
โ๏ธ ์์ด์ฌ์ ์ ๋ชจ๋ ๋จ์ด๋ฅผ Dict๋ List์ ๋ด๋๋ค๋ฉด ์์ฒญ๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ์๋กํ์ง๋ง, Generator ํจํด์ ํ์ ํ ์ ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด์ ์๋ฃ๊ตฌ์กฐ๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค.
โ๏ธ Generator๊ฐ ๋ ์ ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์๋ ์ด์ ๋ Generator์ next๋ผ๋ ํจ์๊ฐ ํธ์ถ๋ ๋๋ง ๋ฐ์ดํฐ๋ฅผ ํ๋ํ๋ ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์
๋๋ค.
โ๏ธ ๋ํ ๋จ์ ์คํ ๊ฐ๋ฅํ Coroutine์ ์ดํดํ๊ธฐ ์ํด์ Generator๋ฅผ ์ดํดํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
โ๏ธ Generator๋ ์ผ๋ฐ์ ์ผ๋ก ํจ์๋ก ์ฌ์ฉํฉ๋๋ค. ์๋ Generator ํจ์๋ฅผ ํตํด yield์ ํน์ง์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
โ๏ธ ํจ์๊ฐ ํธ์ถ๋์์ ๋, yield๋ฅผ ๋ง๋๋ฉด ๋ฉ์ถ์๋ค๊ฐ ๋ค์ next๋ก ํธ์ถ์ด ์ด๋ค์ง๋ฉฐ ๋ค์ yield๋ฅผ ๋ง๋๊ธฐ ์ ๊น์ง ์๋๋๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
โ๏ธ yield๊ฐ ์คํ ๋, ๋ค์ yield๋ฅผ ๋ง๋ ์์น๋ฅผ ๊ธฐ์ตํ๊ณ ๋๊ธฐํ๊ณ ์๋ ๊ฒ์ด์ฃ .
def generator1(): print("Start!") yield "AAA" print("Continue!") yield "BBB" print("Continue!") yield "CCC" temp = iter(generator1()) # ๐ generator๋ iter ํจ์์ ๋ด์ ์ฌ์ฉํฉ๋๋ค.
print(next(temp)) """ Start! AAA """
print(next(temp)) """ Continue! BBB """
print(next(temp)) """ Continue! CCC """
โ๏ธ Generator๋ ๋ฐ๋ณต๋ฌธ๊ณผ ์ฌ์ฉํ ๋ ์ง๊ฐ๋ฅผ ๋ฐํํฉ๋๋ค. ์ํ ๊ฐ๋ฅํ ์์๊ฐ ์กด์ฌํ ๋ ๊น์ง๋ง ๋ฐ๋ณตํ๊ธฐ ๋๋ฌธ์ด์ฃ .
โ๏ธ ์ฆ, for๋ฌธ์ StopIteration Error๋ฅผ ๋ฐ์์ํค์ง ์๊ฒํด์ค๋๋ค.
โ๏ธ for๋ฌธ์ ์ฌ์ฉํ์ง ์๋๋ค๋ฉด ์์์ฒ๋ผ nextํจ์๋ฅผ ์ฌ๋ฌ๋ฒ ์ด์ฉํด์ผํ๊ณ , StopIteration Error์ด ๋ฐ์๋ ์ ์๊ธฐ ๋๋ฌธ์ Generator๋ for๋ฌธ๊ณผ ํจ๊ป ์ฃผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
for i in generator1(): print(i) """ Start! AAA Continue! BBB Continue! CCC """
โ๏ธ ์ปดํ๋ฆฌํจ์ ๊ณผ Generator๋ฅผ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
temp2 = [i*3 for i in generator1()] print(temp2) """ Start! Continue! Continue! ['AAAAAAAAA', 'BBBBBBBBB', 'CCCCCCCCC'] """ for i in temp2: print(i) """ AAAAAAAAA BBBBBBBBB CCCCCCCCC """
โ๏ธ ํํ์ฒ๋ผ ๊ดํธ๋ก ์ปดํ๋ฆฌํจ์ ์ ์ด์ฉํ๋ฉด Generator๋ฅผ ๋ฐํ์์ผ์ค๋๋ค. ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ , ๋ฐ๋ณตํ ๋ฐ์ดํฐ์ ์ ์ค๋น์์ผ์ค๋๋ค.
temp3 = (i*3 for i in generator1()) print(temp3) # <generator object <genexpr> at 0x7fdd34a01900> for i in temp3: print(i) """ Start! AAAAAAAAA Continue! BBBBBBBBB Continue! CCCCCCCCC """
โ๏ธ Generator์ ๋ค์ํ ํจ์๋ฅผ ์ด์ฉํ๊ธฐ ์ํด์๋ "itertools"๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
โ๏ธ itertools์ count ํจ์๋ start์ stop์ ์ ๋ฌ๋ฐ์ next๊ฐ ์คํํ ๋๋ง๋ค ์ฐ์ฐ์ ์ํํด์ค๋๋ค.
โ๏ธ count๋ ํ๊ณ๋ฅผ ์ ํด๋์ง ์์ ๋ฌดํ์ด๊ธฐ ๋๋ฌธ์ for๋ฌธ์ ๋๋ฆฌ๋ฉด ๋ฌดํํ๊ฒ ์๋ํ๊ธฐ ๋๋ฌธ์ start์ stop์ ํตํด ๋ฒ์๋ฅผ ์ ํด์ค๋๋ค.
import itertools 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
โ๏ธ takewhile()์ ์ฒซ๋ฒ์งธ parameter๋ก ์ข
๋ฃ ์กฐ๊ฑด์ด ๋ช
์๋ ํจ์๋ฅผ ๋ฐ๊ณ , ๋๋ฒ์งธ parameter๋ก generator๋ฅผ ์ ๋ฌํด์ฃผ๋ฉด๋ฉ๋๋ค.
โ๏ธ 1๋ถํฐ 100์ด์ ๊น์ง 2.5์ฉ ์ฆ๊ฐํ๋ฉด์ generator๋ฅผ ์์ฑํฉ๋๋ค.
import itertools gen2 = itertools.takewhile(lambda n: n<100, itertools.count(1, 2.5)) for i in gen2: print(i, end=', ') """ 1, 3.5, 6.0, 8.5, 11.0, 13.5, 16.0, 18.5, 21.0, 23.5, 26.0, 28.5, 31.0, 33.5, 36.0, 38.5, 41.0, 43.5, 46.0, 48.5, 51.0, 53.5, 56.0, 58.5, 61.0, 63.5, 66.0, 68.5, 71.0, 73.5, 76.0, 78.5, 81.0, 83.5, 86.0, 88.5, 91.0, 93.5, 96.0, 98.5 """
โ๏ธ filterfalse()๋ ๋ช
์๋ ์กฐ๊ฑด๊ณผ ๋ฐ๋๋๋ ๊ฐ์ filteringํ์ฌ generator๋ก ์์ฑ ํ ๋ฐํํฉ๋๋ค.
โ๏ธ ์ฒซ๋ฒ์งธ parameter๋ ์กฐ๊ฑด์ ํจ์๋ก ๋ฐ๊ณ , ๋๋ฒ์งธ parameter๋ ๋์ ๋ฒ์๋ฅผ ๋ฐ์ต๋๋ค.
import itertools gen3 = itertools.filterfalse(lambda n: n<5, range(10)) for i in gen3: print(i) """ 5 6 7 8 9 """
โ๏ธ accumulate()๋ ๋ฐ๋ณตํ๋ฉด์ ๊ฐ์ ๋์ ์ํต๋๋ค.
import itertools gen4 = itertools.accumulate(x for x in range(1,11)) for i in gen4: print(i) """ 1 3 6 10 15 21 28 36 45 55 """
โ๏ธ product()๋ tuple ํํ๋ก ์์๋ฅผ ์ชผ๊ฐ์ ๋ฐํํฉ๋๋ค.
โ๏ธ repeat ์ต์
์ ์ฌ์ฉํ๋ฉด ๊ทธ ์๋งํผ ์ชผ๊ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๋ณตํ์ฌ ๋ชจ๋ ๊ฒฝ์ฐ์ ์๋ฅผ generator๋ฅผ ์์ฑํฉ๋๋ค.
import itertools gen5 = itertools.product('ABCDE') print(list(gen5)) # [('A',), ('B',), ('C',), ('D',), ('E',)] gen6 = itertools.product('ABCDE', repeat=2) print(list(gen6)) """ [('A', 'A'), ('A', 'B'), ('A', 'C'), ('A', 'D'), ('A', 'E'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('B', 'D'), ('B', 'E'), ('C', 'A'), ('C', 'B'), ('C', 'C'), ('C', 'D'), ('C', 'E'), ('D', 'A'), ('D', 'B'), ('D', 'C'), ('D', 'D'), ('D', 'E'), ('E', 'A'), ('E', 'B'), ('E', 'C'), ('E', 'D'), ('E', 'E')] """