1. Iterator(이터레이터)
- 순차적으로 데이터에 접근할 수 있는 객체
- 순서대로 다음 값을 리턴할 수 있는 객체
1) Iterable 하다.
- list, dict, tuple 객체는 타입이 다른 여러가지 원소를 가질 수 있습니다.
- 이 객체들은 일반적으로 for loop를 통해 순서대로 원소들을 가져오게 됩니다.
- 이 때, 이 객체를 iterable한 객체라고 합니다.
my_list = [1,2,3,4]
for i in my_list:
print(i)
## 이터레이터는 아니기 때문에 next() 메소드 사용시 에러 발생
next(my_list)
2) Iterator와 차이?
- Iterator -> Iterable 하다!
- Iterator가 아닌 list, tuple 등의 객체 -> Iterable 하다!
- Iterator는 iter() 메소드를 활용하여 만들 수 있으며, 내부에 next() 객체가 존재한다.
- 따라서, Iterable 한 것 중 내부에 next() 객체가 존재하는 것만이 Iterator가 될 수 있다.
- Iterator 객체는 항상 iterable입니다.
my_list = [1,2,3,4]
## 이터레이터로 변경 (list -> iterator)
my_iter = iter(my_list)
## next() 메소드 사용하여 원소에 하나씩 접근 가능
next(my_iter)
3) Iterator를 반환하는 함수들
- map
my_list = [1, 2, 3, 4]
# map 이터레이터
map_iter = map(lambda x: x * 2, my_list)
# print(list(map_iter)) # [2, 4, 6, 8]
next(map_iter)
- filter
my_list = [1, 2, 3, 4]
# filter 이터레이터
filter_iter = filter(lambda x: x % 2 == 0, my_list)
# print(list(filter_iter)) # [2, 4]
next(filter_iter)
- zip
my_list = [1, 2, 3, 4]
# zip 이터레이터
zip_iter = zip([1, 2], ['a', 'b'])
# print(list(zip_iter)) # [(1, 'a'), (2, 'b')]
next(zip_iter)
이 외에도
- itertools.count(start, step): 무한히 증가하는 이터레이터를 생성.
- itertools.cycle(iterable): iterable을 반복하는 이터레이터를 생성.
- itertools.repeat(elem, n): 동일한 값을 n번 반복하는 이터레이터를 생성.
2. Generator
- Iterator의 일종 혹은 특수한 형태로, yield키워드를 사용하여 값을 동적으로 반환한다.
- 동적으로 반환한다. -> 값을 미리 모두 계산해두지 않고, 필요할 때 그때그때 생성해서 반환.
- 그렇기 때문에 Iterable 객체의 크기가 아주 커도 이를 모두 메모리에 올려두고 사용하지 않기 때문에 메모리 효율이 좋아진다.
1) Iterator와 Generator의 공통점
- next() 메서드를 통해 하나씩 값을 반환하며, 값이 없을 때 StopIteration 예외를 발생
2) generator 생성 방법
- 아래와 같이 4가지 방법을 통해 generator 객체를 생성할 수 있다.
## 1
def my_generator():
yield 1
yield 2
yield 3
yield 4
yield 5
gen1 = my_generator()
## 2
gen2 = (i for i in range(1,6))
## 3
def my_generator():
for i in range(1,6):
yield i
gen3 = my_generator()
## 4
def my_generator():
yield from [i for i in range(1,6)]
gen4 = my_generator()
3) Generator를 사용하는 목적
- 메모리 사용량 절약
- Iterator 의 경우 생성시 반복의 대상이 되는 객체 + Iterator 객체 이만큼의 메모리가 소비된다.
- 반면, Generator의 경우 반복의 대상이 되는 객체에 대한 메모리 소비는 없고, Generator 객체에 대한 메모리만 소비된다.
import sys
# 리스트를 사용한 메모리 측정
def create_list(n):
return [i for i in range(n)]
# 제너레이터를 사용한 메모리 측정
def create_generator(n):
for i in range(n):
yield i
# 리스트와 제너레이터 메모리 사용 비교
n = 1000000
list_data = create_list(n)
iter_data = iter(list_data)
gen_data = create_generator(n)
print(f"Iteraor memory usage : {sys.getsizeof(list_data) + sys.getsizeof(iter_data)} bytes")
print(f"Generator memory usage : {sys.getsizeof(gen_data)} bytes")
---
Iteraor memory usage : 8448776 bytes
Generator memory usage : 112 bytes
- 초대용량 데이터를 대상으로 반복문 실행이 필요할 때
- 굉장히 큰 데이터를 대상으로 일정한 반복 작업이 필요할 때, 일반적인 for문을 사용하게 되면 일단 데이터를 메모리에 올리는 작업이 선행된다. 이 경우 서버의 리소스가 부족할 경우 작업이 취소되고, 리소스 여유가 있더라도 데이터를 읽어들이는데 시간이 많이 소요될 수 있다.
- 이 때, 데이터를 대상으로 Generator를 생성하여 내부 원소들에 대한 반복 작업 처리를 한다면 리소스 및 시간을 많이 절약할 수 있다.
- 무한한 자연수를 대상으로 반복문을 실행
# 무한한 자연수를 생성하는 제너레이터
def infinite_sequence():
num = 0
while True:
yield num
num += 1
# 제너레이터 사용
gen = infinite_sequence()
for i in range(5): # 무한 루프에서 5개만 출력
print(next(gen))
- 대용량 csv 데이터에 대한 pandas dataframe 형상을 보고자 할 때
- 특정 csv 파일을 pandas로 읽어와 dataframe 형상을 보고싶을 때, csv 파일이 너무 방대하면 read_csv 하는데 많은 시간이 걸리거나, 읽어오지 못하는 경우가 생길 수 있다.
- Generator를 활용하면 데이터의 특정 행까지만 불러와 형상 파악이 가능하다.
import pandas as pd
## 데이터의 10행 까지만 가져오는 Generator 생성
def get_df():
df = pd.read_csv('dataset.csv')
for i in range(10):
yield df.iloc[i]
gen = get_df()
## generator에서 데이터를 불러와 DataFrame 생성
pd.DataFrame(list(gen))
'Sprint_DA01 > 보충학습' 카테고리의 다른 글
241018 보충학습 #2 Decorator (0) | 2024.11.29 |
---|---|
241115 보충학습 #4 F-String (2) | 2024.11.23 |
241122 보충학습 #5 Error and Exception (0) | 2024.11.22 |