이터레이터(Iterator)
- 파이썬에서 반복 가능한 객체(클래스)를 표현하는 데 사용되는 인터페이스
- __iter__() : 이터레이터 객체 자체를 반환
- __next__() : 다음 요소를 반환, 더 이상 반환할 요소가 없으면 raise StopIteration 예외를 발생시켜 순회를 종료
- 이터레이터는 init() 함수와 next() 함수를 이용하여 반복(Iterator)을 수행함
- 이터레이터 기능은 반복문(for or while)을 사용해야만 작동하는 기능
최초 __iter__() 함수를 호출하고, 출력 시 __next__() 함수가 한번씩 수행하면서 값을 반환받아서 출력함
한번 반환된 후 메모리는 초기화되며, 다음 반복시 다시 메모리 사용 - 메모리를 효율적으로 활용할 수 있음
- java의 readLine()이랑 nextLine()와 비슷!!
< 이터레이터 기본 구조>
### 클래스 정의하기
class MyIterator:
### 클래스 생성자 정의하기
def __init__(self, start, end):
self.current = start
self.end = end
### 자신의 클래스를 반환하는 iter 함수 정의
def __iter__(self):
return self
### 반복을 수행하는 next 함수 정의
def __next__(self):
if self.current <= self.end:
result = self.current
self.current += 1
return result
else:
raise StopIteration
# 이터레이터 객체 생성
my_iterator = MyIterator(1, 5)
# 이터레이터 순회
for item in my_iterator:
print(item)
※ 이터레이터는 반복이 끝나면 종료시켜야함
종료시키는 방법은 강제로 오류 발생시킴 : raise StopIteration
< 이터레이터 실습1>
<예제> 0부터 4까지의 숫자를 생성해서 반환하는 이터레이터 만들기
class MyIterator:
def __init__(self):
self.current_value = 0
print(f"#1(__init__) : self={self} / self.current_value = {self.current_value}")
def __iter__(self):
print(f"#2(__iter__) : self={self}")
return self
def __next__(self):
print(f"#3(__next__) : self={self}")
### current_value의 값이 5보다 작을 때까지 반복 수행
if self.current_value < 5:
# 반환할 변수에 current_value의 현재값 저장
result = self.current_value
# current_value의 현재값을 1증가
self.current_value += 1
print(f"#4(__next__) : result={result} / self.current_value = {self.current_value}")
# result 값 반환 (최초는 0)
return result
else:
print("#5(else) : StopIteration 예외 발생!!")
raise StopIteration
<이터레이터 실행시키기>
1번 방법 반복하여 전체 실행하기
- 클래스 생성하기
my_iterator = MyIterator()
결과
#1(__init__) : self=<__main__.MyIterator object at 0x0000028D8FC2BFD0> / self.current_value = 0
반복 수행하여 result 값 출력하기
for value in my_iterator:
print(value)
결과
#2(__iter__) : self=<__main__.MyIterator object at 0x0000028D8FC2BFD0>
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8FC2BFD0>
#4(__next__) : result=0 / self.current_value = 1
0
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8FC2BFD0>
#4(__next__) : result=1 / self.current_value = 2
1
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8FC2BFD0>
#4(__next__) : result=2 / self.current_value = 3
2
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8FC2BFD0>
#4(__next__) : result=3 / self.current_value = 4
3
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8FC2BFD0>
#4(__next__) : result=4 / self.current_value = 5
4
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8FC2BFD0>
#5(else) : StopIteration 예외 발생!!
<이터레이터 실행시키기>
2번 방법 한 건, 한 건 실행하기
- 클래스 생성하기
my_iterator = MyIterator()
결과
#1(__init__) : self=<__main__.MyIterator object at 0x0000028D8F603210> / self.current_value = 0
print(next(my_iterator))
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8F603210>
#4(__next__) : result=0 / self.current_value = 1
0
try:
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
except:
print("이터레이터가 종료되었습니다.")
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8F603210>
#4(__next__) : result=1 / self.current_value = 2
1
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8F603210>
#4(__next__) : result=2 / self.current_value = 3
2
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8F603210>
#4(__next__) : result=3 / self.current_value = 4
3
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8F603210>
#4(__next__) : result=4 / self.current_value = 5
4
#3(__next__) : self=<__main__.MyIterator object at 0x0000028D8F603210>
#5(else) : StopIteration 예외 발생!!
이터레이터가 종료되었습니다.
※ 안에있는 모든 자원을 다 사용하고 나면 오류나게 설정했으므로 try-except해줌!!
< 이터레이터 실습2>
<예제> 문자열을 전달 받아서 문자 하나씩 추출하여 반환하는 이터레이터 생성하기
- 문자열 "Hello"의 각 문자 하나씩 출력하는 프로그램을 작성(기본)
str = "Hello"
for i in str:
print(i)
- 이터레이터 클래스 생성해서 Hello 각 단어 출력하기
클래스 이름 : StringIterator
임의의 문자열을 받아서 처리한다. : str2 = "Hello"
임의의 문자열은 외부에서 클래스 생성시 넣어준다.
class StringIterator:
def __init__(self, p_word):
self.word = p_word
# next함수에서 1씩 증가시키면서 반복 조건에 사용할 변수
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.word):
result = self.word[self.index]
self.index += 1
return result
else:
raise StopIteration
<이터레이터 실행시키기>
1번 방법 반복하여 전체 실행하기
- 클래스 생성하기
str2 = "Hello"
str_iterator = StringIterator(str2)
- 반복 수행하여 전체 출력하기
for v in str_iterator:
print(v)
결과
H
e
l
l
o
※ 클래스 생성할 때 값을 넣고 싶으면 init에 매개변수 넣어주기!!!!
※ 받아온 문자열 구분
- self.word (멤버변수) : 클래스 내 공간이 할당되어있음
- p_word (지역변수, 매개변수) : 받아온 값
※ 반복수행을 위한 iter 함수 정의 : 무조건 자기자신의 class자원을 반환
※ 한건 한건 처리를 위한 next함수 정의 : 조건문 넣어줘야함, 조건 만족하지 않으면 오류 발생하도록(StopIteration)
< 이터레이터 실습3>
<예제> 일반 프로그래밍 방식과 이터레이터 방식의 메모리 비교
- 메모리 확인을 위한 라이브러리 설치 필요 ( profiler )
- prompt에서 conda activate gj_env_01 하고서 pip install memory-profiler
- (실행이 안돼서 base로 가서 설치)
- conda deactivate 하고서 pip install memory-profiler
주피터노트북
- from + [폴더명] import + [.py의 파일명]
### from + [폴더명] import + [.py의 파일명]
from memory_profiler import profile
- 주피터노트북에서는 아래 로드 처리 해야함 ( %load_ext )
%load_ext memory_profiler
- 이터레이터 클래스 생성하기
class SimpleIterator:
def __init__(self, limit):
#반복 범위를 지정할 값(반복의 끝값)
self.limit = limit
#반복 시작값
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.limit:
self.current += 1
return self.current
else:
raise StopIteration
<데코레이터 함수 정의하기>
- profile의 라이브러리는 데코레이터 자동으로 만들 수 있다. ( @profile )
### 이터레이터를 사용하지 않고 메모리 체크하기
@profile
def no_iterator(limit):
data = [i for i in range(1, limit+1)]
### 이터레이터를 사용해서 메모리 체크하기
@profile
def yes_iterator(limit):
data = SimpleIterator(limit)
for item in data:
###반복만 처리하고 별도 출력은 안함
pass
- %memit : 외부자원을 사용할 때 사용(주피터노트북만 사용!)
limit = 1000000
try:
### 데코레이터 함수 호출하기
%memit no_iterator(limit)
### 이터레이터 함수 호출하기
%memit yes_iterator(limit)
except:
pass
결과
ERROR: Could not find file C:\Users\user\AppData\Local\Temp\ipykernel_22464\3269425511.py
peak memory: 104.99 MiB, increment: 25.90 MiB
ERROR: Could not find file C:\Users\user\AppData\Local\Temp\ipykernel_22464\3269425511.py
peak memory: 80.27 MiB, increment: 0.02 MiB
※ no_iterator는 메모리 증가가 25.9MiB
※ yes_iterator는 메모리 증가가 0 MiB
- 따라서, 이터레이터를 사용하면 메모리 효율이 좋다는 것을 알 수 있다.
※ 주피터노트북에서만 쓰는 것 : %load_ext, %memit
파이썬
- 04_iterator_memory_test.py 파일 만들기
- 04_iterator_memory_test.py 코드작성 (주피터노트북에서 작성)
from memory_profiler import profile
class SimpleIterator:
def __init__(self, limit):
#반복 범위를 지정할 값(반복의 끝값)
self.limit = limit
#반복 시작값
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.limit:
self.current += 1
return self.current
else:
raise StopIteration
### 이터레이터를 사용하지 않고 메모리 체크하기
@profile
def no_iterator(limit):
data = [i for i in range(1, limit+1)]
### 이터레이터를 사용해서 메모리 체크하기
@profile
def yes_iterator(limit):
data = SimpleIterator(limit)
for item in data:
###반복만 처리하고 별도 출력은 안함
pass
if __name__ == "__main__":
limit = 1000000
print("no_iterator -------------------\n")
no_iterator(limit)
print("yes_iterator -------------------\n")
yes_iterator(limit)
pass
- 가상환경 들어가기
> conda activate gj_env_01
- 파일있는 위치로 이동
> cd C:\Users\user\gj_202311\02_파이썬_알고리즘
- 프롬프트에서 실행시 명령어
> python -m memory_profiler 04_iterator_memory_test.py
결과
no_iterator는 메모리가 83.4MiB 쓰이고 있고
yes_iterator는 메모리가 45.3MiB 쓰인다.
따라서, 이터레이터를 사용하면 메모리 효율이 좋다는 것을 알 수 있다.
※ 파이썬파일 실행시키려면 if __name__ == "__main__": 사용!!
< 이터레이터 실습4>
<예제> 두 개의 숫자(시작값, 종료값) 값을 이용해서, 짝수값만 반환하는 이터레이터 만들기
- 이터레이터 클래스 : EvenNumberIterator
class EvenNumberIterator:
def __init__(self, start, end):
self.start_num = start
self.end_num = end
def __iter__(self):
return self
def __next__(self):
for i in range(self.start_num, self.end_num, 1) :
### self.start_num가 짝수인지 체크
if self.start_num % 2 == 0:
# 반환할 변수에 저장
result = self.start_num
# self.start_num 값은 1증가
self.start_num += 1
# 반환하기 : 반환하면 for문은 종료됨
return result
### 짝수가 아니면
else:
# 1증가만 시키고 반복을 계속 수행
self.start_num += 1
### for문을 이용한 경우에는, 이터레이터 반복 종료 후 마지막에 아래 추가
raise StopIteration
even_iter = EvenNumberIterator(1, 10)
for v in even_iter:
print(v)
결과
2
4
6
8
※ start부터 end까지 무조건 반복 실행
※ i값은 사용 안함
※ 사용하는 값은 self.start_num만 사용
※ 짝수가 아니여도 self.start_num값 증가시켜주기!!
※ for문을 이용한 경우에는, 이터레이터 반복 종료 후 마지막에 아래 raise StopIteration 추가
< 이터레이터 실습5>
<예제> 외부 함수를 이용해서 짝수값 추출하는 이터레이터 만들기
- 이터레이터 클래스 : EvenNumberIterator
class EvenNumberIterator:
### 클래스 생성자 정의
def __init__(self, start, end, func):
#시작값
self.start = start
#종료값
self.end = end
#외부 함수
self.func = func
###반복을 위한 이터레이터 함수 정의
def __iter__(self):
return self
###반복 결과값을 처리할 함수 정의
def __next__(self):
#시작부터 종료까지 while문 반복
while self.start <= self.end:
#외부함수로 짝수(True) or 홀수(False) 체크
if self.func(self.start):
result = self.start
self.start += 1
return result
else:
self.start += 1
#이터레이터 종료하기
raise StopIteration
- 짝수와 홀수를 판별하는 외부함수 정의하기
def is_even(num):
#리턴값 맞으면 True반환, 틀리면 False반환
return num % 2 == 0
- 클래스 생성하기
even_iter = EvenNumberIterator(1, 10, is_even)
for v in even_iter:
print(v)
결과
2
4
6
8
10
※ 외부함수가져올 func 매개변수 생성하기!
< 이터레이터 실습6>
<예제> 텍스트 파일의 내용을 한줄씩 반환하는 이터레이터 만들기
- 이터레이터 클래스 : FileLineIterator
- 파일이름은 외부에서 받아서 open시키기
- 텍스트 파일 생성 : 04_example.txt ("C:\Users\user\gj_202311\02_파이썬_알고리즘\04_example.txt")
내가 쓴 코드
class FileLineIterator:
def __init__(self, path):
self.path = path
self.f = open(self.path, 'r', encoding = "utf-8")
def __iter__(self):
return self
def __next__(self):
result = self.f.readline()
if result != "":
return result
else:
self.f.close()
raise StopIteration
file_read = FileLineIterator('04_example.txt')
for v in file_read:
print(v)
결과
오늘의 저녁은??
백미밥
순두부백탕
카레소스
치킨너겟/강정소스
배추김치
교수님이 쓴 코드
class FileLineIterator1:
def __init__(self, file_path):
self.file_path = file_path
self.file = open(file_path, "r", encoding = "utf-8")
def __iter__(self):
return self
def __next__(self):
line = self.file.readline()
if line:
return line.strip()
else:
self.file.close()
raise StopIteration
file_path = "./04_example.txt"
file_reading = FileLineIterator1(file_path)
for v in file_reading:
print(v)
결과
오늘의 저녁은??
백미밥
순두부백탕
카레소스
치킨너겟/강정소스
배추김치
※ UnicodeDecodeError : 파일 open 시기에서 읽어오지 못한다는 것, 따라서 utf-8로 encoding해줘야한다.
※ 파일 읽어오기
- 방법1 : open()
r : 읽기모드(텍스트 모드)
f = open('foo.txt', 'r')
f.readline()
f.close()
- 방법2 : with open()
with open('./foo.txt', 'r') as f:
print(f.readline())
※ 방법2 는 이터레이터에서 사용x
why? '파일을 읽어들이고 다 읽어들이면 자동 종료시켜라'
파일 읽고 바로 close되므로 여기서는 사용못함 ( close()를 못하니까 )
※ readline() : '파일 정보중에 한 줄 가지고 와'
- 최초 이후부터는 다음 줄이 있는지 자동으로 체크 후 가지고 온다.
- 다음 줄이 없으면 안가지고 온다.
※ strip() : 문자열에서 공백 제거
이터레이터랑 비슷한 자바 문법이 readLine()이랑 nextLine()인데 이터레이터를 공부했으니
자바의 readLine()이랑 nextLine()를 복습해보자!
readLine
- readLine (BufferedReader 클래스) : BufferedReader 클래스의 메소드로, 주로 텍스트 파일이나 다른 입력 스트림에서 한 줄을 읽을 때 사용.
- readLine 메소드는 예외처리( try - catch )를 해주어야 한다.
< readLine 기본 구조>
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Example {
public static void main(String[] args) {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
System.out.println("Enter a line of text:");
String line = reader.readLine();
System.out.println("You entered: " + line);
} catch (IOException e) {
e.printStackTrace();
}
}
}
결과
Enter a line of text: [사용자 입력] // 여기에 사용자가 입력한 문자열을 입력
You entered: [사용자 입력] // 사용자 입력이 출력됨
nextLine
- nextLine (Scanner 클래스) : Scanner 클래스의 메소드로, 주로 키보드 입력이나 다른 입력 소스에서 데이터를 읽을 때 사용
- nextLine 메소드는 예외 처리를 하지 않아도 된다.
< nextLine 기본 구조>
import java.util.Scanner;
public class Example {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter a line of text:");
String line = scanner.nextLine();
System.out.println("You entered: " + line);
scanner.close();
}
}
결과
Enter a line of text: [사용자 입력] // 여기에 사용자가 입력한 문자열을 입력
You entered: [사용자 입력] // 사용자 입력이 출력됨
'Back-End > Python' 카테고리의 다른 글
[프로그래머스] 코딩테스트 Lv.1 덧칠하기 (5) | 2023.11.23 |
---|---|
[Python] 파이썬 실습2 - 회원전용 도서관리 키오스크 만들기 (4) | 2023.11.17 |
[Python] 파이썬 알고리즘 - 제너레이터(Generator) (1) | 2023.11.16 |
[Python] 파이썬의 기초 - 매개변수, 기본값(default) (2) | 2023.11.14 |
파이썬 실습2 - 도서 입고/대출/반납 관리를 위한 키오스크 만들기 (데코레이터) (1) | 2023.11.14 |