Sign In

[Python] 이터레이터와 제너레이터

Y
Yerim
  1. Python

이터레이터

반복자

반복자 자료형과 유사한 자룧하나 이상의 항목이 포함되어 있는 자료 구조에서 값을 순차적으로 꺼낼 수 있는 객체
next()라는 함수나 객체의 __next__()라는 특수 메소드를 사용

iterable 자료형

l = [10, 20, 30] # 반복 가능 자료형인 리스트
l_iter = iter(l) # <list_iterator object at ...>
n = 100          # 반복 불가능 자료형인 int
n_iter = iter(n) # TypeError발생
반복자 자료형과 유사한 자료형
파이썬의 리스트, 튜플, 문자열, 집합, 파일, range 등
파이썬 내장 함수인 iter() 함수를 사용하여 반복자 객체로 변환
my_list = [1, 2, 3, 4]
my_iter = iter(my_list) # my_list에 __iter__가 호출된다

print(next(my_iter)) # 출력: 1 # my_list에 __next__가 호출됩니다.
print(next(my_iter)) # 출력: 2 # my_list에 __next__가 호출됩니다.
이터레이터가 더 이상 꺼낼 요소가 없을 때 next()를 호출하면 StopIteration 예외 발생
파이썬에서 이터레이터를 만들기 위해 __iter____next__ 메서드를 구현
class MyIterator:
    def __init__(self, stop):
        self.currentValue = 0
        self.stop = stop
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.currentValue >= self.stop:
            raise StopIteration
        result = self.currentValue
        self.currentValue += 1
        return result

my_iterator = MyIterator(5)

for i in my_iterator:
    print(i)

# for는 iter 먼저 실행, next로 StopIteration
my_iterator 객체를 두 번사용하여 다시 순회하는 값으로 만들어 준다
a, b, c, d = MyIterator(4)
a

a, b, c, d = range(4)
a

내장함수

내장 함수
설명
all()
반복 가능한 항목들이 모두 참일 때만 참 반환
any()
임의의 반복 가능한 항목 중 참이 하나라도 있으면 참을 반환
ascii()
주어진 객체를 표현할 수 있는 ASCII 문자열을 반환
bool()
반복 가능 객체의 존재 여부를 부울 값으로 변환
filter()
반복 가능한 항목들을 하나하나 꺼내어 함수를 넣는다
리턴 값이 참인 것만 묶어서 반환하는 함수
(일반적으로 람다 함수를 필터링할 때 사용)
iter()
반복 가능한 객체를 iterator로 반환

제너레이터

def mygen():
    yield 'a'
    yield 'b'
    yield 'c'

g = mygen()
next(g)
이터레이터를 생성하는 특별한 종류의 함수
제너레이터는 yield 키워드를 사용하여 값을 ‘생성’
이터레이터와 마찬가지로 next() 함수를 통해 값을 순차적으로 꺼냄
모든 값을 메모리에 저장하는 대신 사용할 때마다 값을 생성
메모리를 효율적으로 사용할 수 있다 → 큰 시퀀스 생성 시 성능 개선

제너레이터 표현식

# generator.py
def mygen():
    for i in range(1, 1000):
        result = i * i
        yield result

gen = mygen()

print(next(gen)) # 1
print(next(gen)) # 4
print(next(gen)) # 9
next를 호출할 때마다 for문의 결과를 반환한다
gen = (i * i for i in range(1, 1000)) 는 mygen()과 같은 기능을 한다
리스트 컴프리헨션 구문과 유사하지만 튜플을 사용한다는 점이 다르다
for문에서 필요할 때마다 값을 받아오고, 메모리에서 보관하지 않는다 (lazy evaluation)
# 제너레이터 컴프렌션에서 사용했던 문법을 함수형태의 제너레이터로 구현
def count():
    count = 2
    while True:
        yield cont
        count += 2

for i, j in zip(range(10), count()):
    print(i, j)
def count_up_to(n):
    count = 1
    while count <= n:
        yield count
        count += 1

count = count_up_to(5)

print(next(counter)) # 출력: 1
print(next(counter)) # 출력: 2
제너레이터도 더 이상 꺼낼 요소가 없을 때 next()를 호출하면 StopIteration예외 발생
def my_generator(data):
    for i in data
        yield i

my_list = [1, 2, 3, 4, 5]
my_iterator = my_generator(my_list)

for i in my_iterator:
    print(i)
def infinite_generator():
    i = 0
    while True:
        yield i
        i += 1

my_iterator = infinite_generator()

for i in my_iterator:
    print(i)
    if i >= 10:
        break
infinite_generator 함수는 while문을 사용하여 무한 반복
yield 키워드를 사용하여 제너레이터 생성
my_iterator 객체를 for반복문으로 순회하면서 값이 출력
break 키워드를 사용하여 10번째 이후의 값은 출력하지 않는다
파이썬 zip
두 개 이상의 iterable 객체를 인자로 받아서 각 iterable 객체의 동일한 index에 위치한 요소들을 묶어서 tuple 형태로 반환
zip 함수는 iterator를 반환하여, 한 번만 사용할 수 있다
zip 함수가 한 번 호출되면 모든 요소를 반환, iterator가 소멸
zip 객체를 다시 사용하려면 새로운 iterator를 생성해야 한다
a = [1, 2, 3]
b = ['a', 'b', 'c']

z = zip(a, b)
print(list(Z)) # [(1, 'a'), (2, 'b'), (3, 'c')]

# zip 객체는 한 번 사용되었으므로 빈 리스트가 반환
print(list(z)) # []
zip 객체를 생성한 후 list 함수를 통해 출력, zip 객체는 이미 소멸되어 빈 리스트 반환
zip 함수를 여러 번 사용해야하는 경우 각각 새로운 zip 객체를 생성하여 사용
li = [1, 2, 3]
st = ['a', 'b', 'c']
z = zip(li, st)

for i in z:
    print(i)

for i in z:
    print(i)
문자열, rang, list 등의 이터러블 객체는 동일하게 StopIteration이 발생하더라도 다시 호출 가능
li = [1, 2, 3]
i = iter(li)
next(i)
next(i)
next(i)
next(i) # StopIteration
zip, map, reversed, filter가 동일하게 작동
재순회할 수 있다
li = [1, 2, 3]
z = map(lambda x:x**2, li)

for i in z:
    print(i)

for i in z:
    print(i)
li = [1, 2, 3]
z = reversed(lambda x:x**2, li)

for i in z:
    print(i)

for i in z:
    print(i)
sorted는 2번의 for문 모두 순회 가능
# 계속 순회가 가능하게 설계까 되어 있습니다.
# 이런 설계를 위에서 우리가 직접 해보게 될 것입니다.
li = [1, 2, 3]
z = sorted(li)

for i in z:
    print(i)

for i in z:
    print(i)
제너레이터 컴프리헨션 Generator Comprehensions
gen = (x**2 for x in range(10))

print(next(gen)) # 출력: 0 (0의 제곱)
print(next(gen)) # 출력 :1 (1의 제곱)

제너레이터 사용 시 주의사항

def create_gen():
    alist = range(1, 4)
    for x in alist:
        yield x

my_generator = create_gen()
print(my_generator)
for n in my_generator:
    print(n)
for n in my_generator:
    print(n)
제너레이터를 실행할 떄 함수의 몸체를 실행하는 것이 아니라 함수가 가진 객체를 반환
한 번 생성해서 반환한 객체를 보관하지 않기 때문에 이전 코드 실행 후 추가한 코드를 실행하면 아무 객체도 출력하지 않는다

제너레이터 장점

제너레이터를 메모리를 절약해준다
제너레이터는 수행시간도 절약
100만개의 피보나치 수열을 출력하고 싶은 경우
100만개의 피보나치 수열을 생성해서 리스트에 넣고 for문을 통해 출력
100만개의 수열을 계산할 때마다 for문에 넘겨준다 (제너레이터를 사용)
# 일반 함수를 사용한 루프
import time

# function version
def fibon(n):
    a = b = 1
    result = []
    for i in range(n):
        result.append(a)
        a, b = b, a + 
    return result

start_t = time.time()
for x in fibon(1000000):
    pass
    
end_t = time.time()
print('total time = ', end_t - start_t)
# 제너레이터를 사용한 루프
import time

# generator version
def fibon(n):
    a = b = 1
    for i in range(n):
        yield a
        a, b = b, a + b

start_t = time.time()
for x in fibon(1000000):
    pass

end_t = time.time()
print('total time = ', end_t - start_t)
Yerim-DevNote
Subscribe to 'Yerim-DevNote'
Subscribe to my site to be the first to receive notifications and emails about the latest updates, including new posts.
Join Slashpage and subscribe to 'Yerim-DevNote'!
Subscribe
👍