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

## 이터레이터

---

### 반복자

![Image](https://upload.cafenono.com/image/slashpagePost/20250128/002719_V2NV9SXkOcLfA6Xp5T?q=80&s=1280x180&t=outside&f=webp)

- 반복자 자료형과 유사한 자룧하나 이상의 항목이 포함되어 있는 자료 구조에서 값을 순차적으로 꺼낼 수 있는 객체

- `next()`라는 함수나 객체의 `__next__()`라는 특수 메소드를 사용

### iterable 자료형

```javascript
l = [10, 20, 30] # 반복 가능 자료형인 리스트
l_iter = iter(l) # <list_iterator object at ...>
n = 100          # 반복 불가능 자료형인 int
n_iter = iter(n) # TypeError발생
```

- 반복자 자료형과 유사한 자료형

- 파이썬의 리스트, 튜플, 문자열, 집합, 파일, range 등

- 파이썬 내장 함수인 `iter()` 함수를 사용하여 반복자 객체로 변환

```javascript
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__` 메서드를 구현

```javascript
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 객체를 두 번사용하여 다시 순회하는 값으로 만들어 준다

```javascript
a, b, c, d = MyIterator(4)
a

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

### 내장함수

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

## 제너레이터

---

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

g = mygen()
next(g)
```

- 이터레이터를 생성하는 특별한 종류의 함수

- 제너레이터는 `yield` 키워드를 사용하여 값을 ‘생성’

- 이터레이터와 마찬가지로 next() 함수를 통해 값을 순차적으로 꺼냄

- 모든 값을 메모리에 저장하는 대신 사용할 때마다 값을 생성

- 메모리를 효율적으로 사용할 수 있다 → 큰 시퀀스 생성 시 성능 개선

### 제너레이터 표현식

```javascript
# 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)

```javascript
# 제너레이터 컴프렌션에서 사용했던 문법을 함수형태의 제너레이터로 구현
def count():
    count = 2
    while True:
        yield cont
        count += 2

for i, j in zip(range(10), count()):
    print(i, j)
```

```javascript
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`예외 발생

```javascript
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)
```

```javascript
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를 생성해야 한다

```javascript
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 객체를 생성하여 사용

```javascript
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이 발생하더라도 다시 호출 가능

```javascript
li = [1, 2, 3]
i = iter(li)
next(i)
next(i)
next(i)
next(i) # StopIteration
```

- zip, map, reversed, filter가 동일하게 작동

- 재순회할 수 있다

```javascript
li = [1, 2, 3]
z = map(lambda x:x**2, li)

for i in z:
    print(i)

for i in z:
    print(i)
```

```javascript
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문 모두 순회 가능

```javascript
# 계속 순회가 가능하게 설계까 되어 있습니다.
# 이런 설계를 위에서 우리가 직접 해보게 될 것입니다.
li = [1, 2, 3]
z = sorted(li)

for i in z:
    print(i)

for i in z:
    print(i)
```

**제너레이터 컴프리헨션 Generator Comprehensions**

```javascript
gen = (x**2 for x in range(10))

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

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

```javascript
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문에 넘겨준다 (제너레이터를 사용)

```javascript
# 일반 함수를 사용한 루프
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)
```

```javascript
# 제너레이터를 사용한 루프
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)
```

For the site tree, see the [root Markdown](https://slashpage.com/yerim-devnote.md).
