1. ์์ฑ๊ธฐ(generator)๋?
์ ๋๋ ์ดํฐ๋ผ๊ณ ๋ถ๋ฅด๋ ์ด ํจ์๋ ์ฌ๋ฌ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋์ง ์๊ณ ํ์ํ ๋๋ง๋ค ๋ง๋ค์ด๋ด๋ ๊ฐ์ฒด๋ฅผ ์๋ฏธํ๋ค. ์ดํฐ๋ ์ดํฐ(iterator)๋ฅผ ์์ฑํ๋ฉฐ, ํจ์ ์์ ํ๋ ์ด์์ yield ํค์๋๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌํํ ์ ์๋ค.
- generator๋ iterableํ ์์๊ฐ ์ง์ ๋๋ค. (๋ฐ๋ณต ๊ฐ๋ฅํ๋ฉฐ ์์๊ฐ ์์)
- ํ์์ ๋ฐ๋ผ ๊ณ์ฐ๋๊ธฐ ๋๋ฌธ์ '๋์จํ๊ฒ' ํ๊ฐ๋๋ค. → ๊ฒ์ผ๋ฅธ ๋ฐ๋ณต์(lazy iterator)๋ผ๊ณ ๋ถ๋ฆฐ๋ค.
- ๋ฌดํํ ์์๊ฐ ์๋(๋ช ํํ ๋์ด ์๋) ๊ฐ์ฒด(๋ฐ์ดํฐ ์คํธ๋ฆผ)๋ฅผ ๋ชจ๋ธ๋งํ ์ ์๋ค.
- ํจ์์ ๋ด๋ถ ๋ก์ปฌ ๋ณ์๋ฅผ ํตํด ๋ด๋ถ ์ํ๊ฐ ๊ณ์ ์ ์ง๋๋ค.
- ์์ฐ์ค๋ฌ์ด ์คํธ๋ฆผ ์ฒ๋ฆฌ๋ฅผ ์ ํ์ดํ๋ผ์ธ์ผ๋ก ๊ตฌ์ฑํ ์ ์๋ค.
๋ฐ์ดํฐ๋ฅผ ์์ฑํ๊ณ ๋ฐํํ์ฌ ์ถ๋ ฅํ๋ ํ๋ก๊ทธ๋จ์ด ์๋ค๊ณ ํ ๋, ๋ฐ์ดํฐ ํ๋๋น ์ถ๋ ฅ์ ์์๋๋ ์๊ฐ์ด 1์ด๋ผ๋ฉด ์ผ๋ฐ์ ์ธ ํจ์์์๋ (๋ฐ์ดํฐ ๊ฐ์ * 1์ด)์ ์๊ฐ์ด ์์๋ ๊ฒ์ด๋ค. ๋ฐ์ดํฐ๊ฐ 5๊ฐ์ด๋ฉด 5์ด ๋ค 5๊ฐ์ ๋ฐ์ดํฐ๊ฐ ํ ๋ฒ์ ์ถ๋ ฅ๋๋ค.
def default_func():
default_data = [] # appendํ ๋ฆฌ์คํธ
for f in "12345": # 5๊ฐ์ data
time.sleep(1) # 1์ด ๋๊ธฐ
default_data.append(f) # data append
return default_data # ๋ฐํ
for i in default_func(): # fuction ํธ์ถ
print(i)
default_func ํจ์๋ฅผ ์ ์ธํ๊ณ , for๋ฌธ์ ํตํด ๋ฐํ๋ ๊ฐ์ ์ถ๋ ฅํด๋ณด๋ฉด,
# after 5 second
1
2
3
4
5
์คํ ์๊ฐ ๊ธฐ์ค์ผ๋ก 5์ด๊ฐ ์ง๋ ๋ค "12345"๊ฐ ๋์์ ์ถ๋ ฅ๋๋ ๊ฒฐ๊ณผ๋ฅผ ๋ณผ ์ ์๋ค. 5๊ฐ์ ๋ฌธ์๋ฅผ ์ถ๋ ฅํ ๋๋ ํฐ ๋ฌธ์ ๊ฐ ์์ง๋ง, ์๋ง ๊ฐ ์ด์์ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ ์๋ง ์ด์ ์๊ฐ์ด ์์๋ ๊ฒ์ด๋ค. ์ด๋ด ๋ ์ ๋๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ค ์ฅ์ ์ ์ป์ ์ ์์๊น?
def yield_func():
for f in "12345":
time.sleep(1)
yield f
for i in yield_func():
print(i)
์์์ ์์ฑํ default_func()์ ๊ธฐ๋ฅ์ yield๋ฅผ ์ฌ์ฉํ generator๋ก ๊ตฌํํ ๊ฒฐ๊ณผ์ด๋ค. ์ด ํจ์์ ์คํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด,
# after 1 second
1
# after 1 second
2
# after 1 second
3
# after 1 second
4
# after 1 second
5
๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ ๋ฐ 1์ด๊ฐ ์์๋๋ ํจ์์์ 5์ด ๋ค ๋ชจ๋ ๊ฐ์ ํ ๋ฒ์ ์ถ๋ ฅํ์ง ์๊ณ ๊ฐ๋ณ์ ์ผ๋ก ์ถ๋ ฅ๋๋ค. 1์ด๊ฐ ์์๋ ํ 1์ด ์ถ๋ ฅ๋๊ณ , ๋ค์ 1์ด๊ฐ ์์๋ ํ 2๊ฐ ์ถ๋ ฅ, ๋ค์ 1์ด๊ฐ ์์๋ ํ 3์ด ์ถ๋ ฅ๋๋ ํ์์ผ๋ก 5๊น์ง ์ถ๋ ฅ๋๋ค. ๊ฒฐ๊ณผ๋ฅผ ๋๋์ด์ ์ป์ ์ ์๋ ๊ฒ์ด๋ค.
์ ๋๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ์ฑ๋ฅ ์ธก๋ฉด์์ ์ด์ ์ ์ป๋๋ค.
๋ํ return์ผ๋ก ๋ฐํํ ๊ฒฝ์ฐ ๋ชจ๋ ๊ฒฐ๊ณผ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ ค๋์๋ค ๋ฐํํ์ง๋ง, yield ํค์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฒฐ๊ณผ๊ฐ์ ํ๋์ฉ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ ค๋๊ณ ๋ฐํํ๋ค.
์ ๋๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋ชจ๋ฆฌ ํจ์จ ์ธก๋ฉด์์ ์ด์ ์ ์ป๋๋ค.
๋๋ฌธ์ ์ ๋๋ ์ดํฐ๋ ๋ฉ๋ชจ๋ฆฌ์ ํ ๋ฒ์ ์ฌ๋ฆฌ๊ธฐ ๋ถ๋ด์ค๋ฌ์ด ๋์ฉ๋ ํ์ผ์ ์ฝ๊ฑฐ๋, ์คํธ๋ฆผ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฑ์ ์์ ์ด ํ์ํ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ์ฌ ๋ ํจ์จ์ ์ธ ํ๋ก๊ทธ๋จ์ ์์ฑํ ์ ์๋ค.
2. yield ํค์๋๋?
ํจ์์์ ์ผ๋ฐ์ ์ผ๋ก ๋ฐํ๊ฐ์ ๋ณด๋ด๊ธฐ ์ํ 'return' ๋์ ์ฌ์ฉํ ์ ์๋ ํค์๋์ด๋ค. ํ์ง๋ง return๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋ฐํ๋์ง๋ ์์ผ๋ฉฐ ๊ฒฐ๊ณผ๊ฐ์ ํ ๋ฒ์ ์ ๊ณตํ๋ ๊ฒ์ด ์๋ ์ฌ๋ฌ ์ฐจ๋ก์ ๋๋์ด ์ ๊ณตํ๋ค.
- yield๋ ํจ์๋ฅผ ์ ์ง์ํค๊ณ ํ ์ํ๋ฅผ ์ ์ฅํ๋ค. (return์ ํจ์๋ฅผ ์ข ๋ฃ์ํด)
- ํจ์๊ฐ ์ฐ์ด์ด ์คํ๋ ๋ ๋ด๋ถ์ ์ ์ฅ๋์ด ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ค.
- ํจ์๊ฐ ํธ์ถ๋๋ฉด iterator ๋ฉ์๋(__iter__, __next__ ๋ฑ)๊ฐ ์๋์ผ๋ก ์ํ๋๋ค.
- next() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ์์ดํ ์ ๋ฐ๋ณต(iterate)์ํฌ ์ ์๋ค.
- yield๋ก ์์ฑํ generator๋ iterableํ ๊ฐ์ฒด๊ฐ ๋๋ฉฐ ๋ฐ๋ณต๋ฌธ์์ ์ฌ์ฉํ ์ ์๋ค.
yield ํค์๋๊ฐ ์คํ๋๋ฉด ํจ์์ ์ํ๋ฅผ ์ ์ฅํ ํ ์ ๋๋ ์ดํฐ๊ฐ ํธ์ถ๋ ํจ์๋ก ์คํ์ ๋๊ธฐ๊ฒ ๋๋ค. ๋๋ฌธ์ yield๋ 'ํน์ ๋ฐ์ดํฐ๋ฅผ ํจ์ ๋ฐ๊นฅ์ผ๋ก ์ ๋ฌ(๋ฐํ)ํ๋ฉด์ ํ๋ก๊ทธ๋จ์ ์คํ์ ํจ์ ๋ฐ๊นฅ์ ์๋ณดํ๋ค'๋ผ๊ณ ๋งํ ์ ์๋ค.
โถ yield from
Python 3.3๋ถํฐ ์ฌ์ฉ ๊ฐ๋ฅํ ํค์๋๋ก, ๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํ์ง ์๊ณ ์ ๋๋ ์ดํฐ๋ฅผ ๋ฐํํ ์ ์๋ค.
>>> def default_generator():
... a = [1, 2, 3]
... for i in a:
... yield i
...
>>> gen = default_generator()
>>> list(gen)
[1, 2, 3]
yield๋ฅผ ์ฌ์ฉํ ์ผ๋ฐ์ ์ธ ์ ๋๋ ์ดํฐ์์๋ ๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํ์ฌ ๋ฐํํ๋ค. (ํ ๋ฒ์ ์ฌ๋ฌ ๊ฐ์ ๋ณด๋ผ ์ ์์)
>>> def yf_generator():
... a = [1, 2, 3]
... yield from a
...
>>> gen = yf_generator()
>>> list(gen)
[1, 2, 3]
ํ์ง๋ง ์์ ๊ฐ์ด yield from ํค์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ๋ณต๋ฌธ ์์ด iterableํ ๊ฐ์ฒด๋ฅผ yieldํ ์ ์๋ค.
3. generator์ yield์ ์์ฉ
generator๋ iterableํ ๊ฐ์ฒด๋ก ๋์์ ๋ ๊ฐ ์์ฑํ ๊ฒฝ์ฐ ์๋ก ๋ค๋ฅธ ๊ฐ์ฒด๋ก์ ๋์ํ๋ค.
>>> def generator_ex():
... yield 1
... yield 2
... yield 3
...
>>> a = generator_ex()
>>> b = generator_ex()
>>> a == b
False
>>> a is b
False
>>> next(a)
1
>>> next(b)
1
>>> next(a)
2
>>> next(b)
2
>>> next(b)
3
>>> next(a)
3
Python์ ์๋ฃํ์ธ List, Set, Dictionary์ ํํ์ ๋ด๋ถ๋ฅผ ์ดํด๋ณด๋ฉด, generator์์ ์ ์ ์๋ค.
>>> type(x*x for x in [2, 4, 6])
<class 'generator'>
generator๋ก ๊ตฌํํ ํจ์๋ฅผ ํธ์ถํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํ๋ ๊ฒ์ด ์๋, ํจ์ ํธ์ถ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๋ก ์ถ๋ ฅํ๋ฉด ์๋์ ๊ฐ์ ๊ฐ์ ๋ฐํํ๋ ๊ฒ์ ์ ์ ์๋ค.
>>> print(return_abc()) # return list("ABC")
['A', 'B', 'C']
>>> print(yield_abc()) # yield "A"; yield "B"; yield "C"
<generator object yield_abc at 0x7f4ed03e6040>
- return_abc() : ๋ฆฌ์คํธ(list)๋ฅผ ๋ฐํํ๋ค.
- yield_abc() : ์ ๋๋ ์ดํฐ(generator)๋ฅผ ๋ฐํํ๋ค.
โถ ์ ๋๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฌดํํ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
๊ฐ๋จํ yield ํค์๋๋ฅผ ์ด์ฉํ์ฌ 1,2,3,4,5๋ผ๋ ์ซ์๋ฅผ ๋ฌดํํ ์์ฑํ๋ ํ๋ก๊ทธ๋จ์ ๋ง๋ค๋ฉด ๋๋ค.
def yield_loop():
while True:
yield 1
yield 2
yield 3
yield 4
yield 5
for f in yield_loop():
print(f)
time ๋ชจ๋์ ์ด์ฉํ์ฌ ํ๋ก๊ทธ๋จ์ ๋๊ธฐํ๊ฑฐ๋, ๋ฐ๋ณต๋ฌธ์ ๋น ์ ธ๋์ค๋ ์กฐ๊ฑด์ด ์๊ธฐ ๋๋ฌธ์ ํ๋ก๊ทธ๋จ์ ๋ฌดํํ ๋ฐ๋ณต๋๋ค.
1
2
3
4
5
1
2
3
4
...(์ค๋ต)
๋ง์ฝ ์ ๋๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๋ฐ์ดํฐ๋ฅผ ๋ฌดํํ ์์ฑํ๋ ํจ์๋ฅผ ์คํํ ๊ฒฝ์ฐ (์ปดํจํฐ์ ๋ฌผ๋ฆฌ์ ์ธ ๋ฉ๋ชจ๋ฆฌ์๋ ํ๊ณ๊ฐ ์กด์ฌํ๊ธฐ ๋๋ฌธ์) ์์คํ ์ ํฐ ๋ถํ๋ฅผ ๋จ๊ธธ ๊ฒ์ด๋ค.