TIL19. Python : Generator ์ด๋ž€?

ID์งฑ์žฌยท2021๋…„ 9์›” 29์ผ
0

Python

๋ชฉ๋ก ๋ณด๊ธฐ
27/39
post-thumbnail

๐Ÿ“Œ ์ด ํฌ์ŠคํŒ…์—์„œ๋Š” Python์˜ Generator์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.



๐ŸŒˆ Generator ์ด๋ž€?

๐Ÿ”ฅ ๋ฐ˜๋ณตํ˜•๊ณผ iter

๐Ÿ”ฅ Generator๋Š” ์™œ ์“ธ๊นŒ์š”?

๐Ÿ”ฅ Generator ๋‚ด์žฅ ํ•จ์ˆ˜



1. ๋ฐ˜๋ณตํ˜•๊ณผ iter

๐Ÿค” ๋ฐ˜๋ณต์ด ๊ฐ€๋Šฅํ•œ ์ด์œ ?

โœ”๏ธ 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


2. Generator๋Š” ์™œ ์“ธ๊นŒ์š”?

๐Ÿค” Generator ๋ž€?

โœ”๏ธ ๋ฐ˜๋ณตํ˜•์—์„œ ๋‹ค์Œ ์ˆœ์„œ์ธ ์š”์†Œ๋ฅผ ๊ฐ€๋ฅดํ‚ค๊ธฐ๋งŒํ•˜๊ณ , 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 ์™œ ์“ธ๊นŒ?

โœ”๏ธ Generator ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ์ง€๋Šฅํ˜• ๋ฆฌ์ŠคํŠธ, ๋”•์…”๋„ˆ๋ฆฌ, ์ง‘ํ•ฉ ๋“ฑ์˜ ๋ฐ˜๋ณตํ˜•์„ ์‚ฌ์šฉํ•  ๋•Œ ๋ณด๋‹ค ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ๊ฐ์†Œ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
โœ”๏ธ ์˜์–ด์‚ฌ์ „์— ๋ชจ๋“  ๋‹จ์–ด๋ฅผ Dict๋‚˜ List์— ๋‹ด๋Š”๋‹ค๋ฉด ์—„์ฒญ๋‚œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ•„์š”๋กœํ•˜์ง€๋งŒ, Generator ํŒจํ„ด์€ ํ˜„์ €ํžˆ ์ ์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
โœ”๏ธ Generator๊ฐ€ ๋” ์ ์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ด์œ ๋Š” Generator์˜ next๋ผ๋Š” ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜ํ•˜๋‚˜ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
โœ”๏ธ ๋˜ํ•œ ๋‹จ์œ„ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ Coroutine์„ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ Generator๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿค” 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
"""   


3. Generator ๋‚ด์žฅ ํ•จ์ˆ˜

๐Ÿค” itertools.count()

โœ”๏ธ 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

๐Ÿค” itertools.takewhile()

โœ”๏ธ 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
"""

๐Ÿค” itertools.filterfalse()

โœ”๏ธ 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
"""

๐Ÿค” itertools.accumulate()

โœ”๏ธ 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
"""

๐Ÿค” itertools.product()

โœ”๏ธ 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')]
"""
profile
Keep Going, Keep Coding!

0๊ฐœ์˜ ๋Œ“๊ธ€