728x90
반응형

<이터레이터(Iterator)>

📍파이썬에서 반복 가능한 객체(클래스)를 표현하는 데 사용되는 인터페이스

📍이터레이터는 init() 함수와 next() 함수를 이용하여 반복(Iterator)을 수행함

📍__iter__() 메서드 

iter 메서드는 이터레이터 객체 자체를 반환한다. 이터레이터 객체는 __iter__() 메서드를 가지고 있어야 한다. 이는 파이썬에서 "순회 가능한(iterable)" 객체의 특징이다.

📍__next__() 메서드

next 메서드는 다음 요소를 반환한다. 만약 더 이상 반환할 요소가 없으면 StopIteration 예외를 발생시켜 순회를 종료시킨다. 이 메서드가 호출될 때마다, 다음 요소를 반환하고 내부 상태를 업데이트하여 다음 호출 시에 이전 요소의 다음 요소를 반환할 수 있도록 한다.

 

반복문 사용하여 이터레이터 사용하기

### 클래스 정의하기

- 아래 코드는 MyIterator 클래스를 정의하고 객체를 생성한 후, for 루프를 사용하여 이터레이터를 순회하고 있다. 이터레이터 객체를 생성하면 __init__ 메서드가 호출되고, 그 후에 __iter__ 메서드가 호출되어 이터레이터 객체를 반환한다. 그리고 for 루프에서 각 단계마다 __next__ 메서드가 호출되어 반복이 수행된다. 반복이 끝나면 StopIteration 예외가 발생하여 순회가 종료된다

### 클래스 정의하기
class MyIterator :
    ### 클래스 생성자 정의하기
    def __init__(self) :
        self.current_value = 0
        print(f"#1 (__init__) : self={self} / self.current_value={self.current_value}")

    ### 자신의 클래스를 반환하는 iter 함수 정의
    def __iter__(self) :
        print(f"#2 (__iter__) : self={self}")
        return self

    ### 반복을 수행하는 next 함수 정의
    def __next__(self) :
        print(f"#3 (__next__) : self={self}")
        ### current_valu의 값이 5보다 작을 때까지 반복 수행
        if self.current_value < 5 :
            # - 반환할 변수에 current_value의 현재값 저장
            result = self.current_value
            # - result 값 반환
            self.current_value += 1

            print(f"#4 : result={result} / self.current_value={self.current_value}")
            # - return 값 반환
            return result
        else :
            print("#5 : StopIteration 예외 발생")
            ### 이터레이터는 반복이 끝나면 종료시켜야함
            # - 종료시키는 방법은 강제로 오류 발생시킴
            raise StopIteration

 

### 이터레이터 실행시키기

- 클래스 생성하기

my_iterator = MyIterator()
#1 (__init__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510> / self.current_value=0

 

### 반복문 실행시키기

- 이터레이터 기능은 반복문(for or while)을 사용해야만 작동하는 기능임

- 최초 __iter__() 함수를 호출하고, 출력 시 __next__() 함수가 한 번씩 수행하면서 값을 반환받아서 출력함

- 한번 반환된 후 메모리는 초기화되며, 다음 반복 시 다시 메모리 사용

- 메모리를 효율적으로 활용할 수 있음

- 반복 수행하여 result 값 출력하기

for value in my_iterator :
    print(value)
#2 (__iter__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#4 : result=0 / self.current_value=1
0
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#4 : result=1 / self.current_value=2
1
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#4 : result=2 / self.current_value=3
2
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#4 : result=3 / self.current_value=4
3
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#4 : result=4 / self.current_value=5
4
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF4FF4510>
#5 : StopIteration 예외 발생

반복문 사용하지 않고 이터레이터 실행시키기

### 이터레이터 실행시키기

- 클래스 생성하기

my_iterator = MyIterator()
#1 (__init__) : self=<__main__.MyIterator object at 0x0000014EF601D110> / self.current_value=0

 

### 반복문 사용하지 않고 실행시키기

print(next(my_iterator))
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF5348E50>
#4 : result=0 / self.current_value=1
0

 

### try / except로 예외처리

try : 
    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 0x0000014EF6549D10>
#4 : result=1 / self.current_value=2
1
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF6549D10>
#4 : result=2 / self.current_value=3
2
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF6549D10>
#4 : result=3 / self.current_value=4
3
#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF6549D10>
#4 : result=4 / self.current_value=5
4

 

- 두 번 실행했을 때 결과

#3 (__next__) : self=<__main__.MyIterator object at 0x0000014EF601D110>
#5 : StopIteration 예외 발생 이터레이터 종료

간단 실습

### 이터레이터 클래스 생성해서 Hello 각 단어 출력하기

- 클래스 이름 : StringIterator

- 임의 문자열을 받아서 처리한다.

- 임의 문자열은 외부에서 클래스 생성 시 넣어준다.

 

내가 만든 코드

class StringIterator:
    def __init__(self, word):
        self.word = word
        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

 

 

- 코드실행

string_iter = StringIterator("안녕하세요")
for v in string_iter:
    print(v)




 

강사님 코드

- p_text 지역변수, 매개변수로써의 의미

- self.text 는 멤버 변수

- 둘이 저장되는 위치가 다름

class StringIterator_:
    ### 클래스 생성자
    def __init__(self, p_text) :
        # - next 함수에서 1씩 증가시키면서 반복 조건에 사용할 변수
        self.index = 0

        # - 받아온 문자열
        self.text = p_text

    ### 반복수행을 위한 iter 함수 정의
    def __iter__(self):
        return self 

    ### 한건 한건 처리를 위한 next 함수 정의
    def __next__(self) :
        if self.index < len(self.text) :
            ### 문자열에서 문자 하나씩 추출하기
            result = self.text[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration

 

 

- 코드 실행

### 이터레이터 기능 사용하기
# 반복문을 이용해서 추출하기
# - 클래스 생성하기
msg = "Hello"
string_iter_ = StringIterator_(msg)

 

 

- 반복문을 이용해서 전체 추출하기

for char in string_iter_ :
    print(char)
H
e
l
l
o

 

- 따로따로 실행하기

print(next(string_iter_))
print(next(string_iter_))
print(next(string_iter_))
print(next(string_iter_))
print(next(string_iter_))
H
e
l
l
o

※ 예외처리를 안 했기 때문에 한번 더 실행하면 오류 발생

일반 프로그래밍 방식과 이터레이터 방식의 메모리 비교

### 메모리 확인을 위해 라이브러리 설치 필요

pip install memory-profiler

 

### 주피터 노트북 ver

## 라이브러리 불러오기

from memory_profiler import profile

### 주피터노트북에서는 아래 로드 처리 해야힘
%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
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

 

## 데코레이터 함수 호출하기

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_3736\513312623.py
peak memory: 130.37 MiB, increment: 25.96 MiB
ERROR: Could not find file C:\Users\user\AppData\Local\Temp\ipykernel_3736\513312623.py
peak memory: 104.40 MiB, increment: 0.00 MiB

 

### 파이썬 ver

새로운 파일에 기존 주피터 노트북에 적은 코드에서 로드해오는 %load_ext memory_profiler 코드 빼고 작성하기

py 형태로 파일 저장한 뒤  프롬프트에서 실행하기

## 데코레이터 함수 호출하는 코드 수정

if __name__ == "__main__" :
    ### 데코레이터 함수 호출하기
    limit = 1000000

    print("no_iterator ------------------------\n")
    no_iterator(limit)
    
    print("yes_iterator ------------------------\n")
    yes_iterator(limit)

 

## 프롬프트에서 실행시 명령어

python -m memory_profiler 04_iterator_memory_test.py

 


간단실습

### 두개의 숫자(시작값, 종료값) 값을 이용해서, 짝수값만 반환하는 이터레이터 만들기

 

내가 만든 코드

class EvenNumberIterator :
    def __init__(self, start, end) :
        self.start = start
        self.end = end

    def __iter__(self) :
        return self

    def __next__(self) :
        if self.start <= self.end and self.start % 2 == 0  :
            self.start % 2 == 0
            result = self.start
            self.start += 2
            return result
        else :
            self.start += 1
            raise StopIteration

 

 

- 코드 실행

num_iterator = EvenNumberIterator(1, 20)
for v in num_iterator :
    print(v)
2
4
6
8
10
12
14
16
18
20

 

강사님 코드

class EvenNumberIterator :
    def __init__(self, start, end) :
        self.start = start
        self.end = end

    def __iter__(self) :
        return self

    def __next__(self) :
        # - 사용하는 값은 self.start 값만 사용
        for i in range(self.start, self.end, 1) :
            ### self.start가 짝수인지 체크
            if self.start % 2 == 0:
                ### 반환할 변수에 저장
                result = self.start
                # - self.start값은 1 증가
                self.start += 1
                # - 반환하기 : 반환하면 for문은 종료됨
                return result
            ### 짝수가 아니면
            else :
                # - 1증가만 시키고 반복을 계속 수행
                self.start += 1
                
        ### for문을 이용한 경우에는, 이터레이터 반복 종류 후 마지막에 아래 추가
        raise StopIteration

 

 

- 코드 실행

even_iter = EvenNumberIterator(1, 10)

for even in even_iter :
    print(even)
2
4
6
8

외부 함수를 이용해서 짝수값 추출하는 이터레이터 만들기

1. 이터레이터 클래스 생성하기

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 :
            ### 외부함수로 짝수 or 홀수 체크
            # - 짝수면 True, 홀수면 False
            if self.func(self.start) :
                result = self.start
                self.start += 1
                return result
            else :
                self.start += 1
        ### 이터레이터 종료하기
        raise StopIteration

 

2. 짝수와 홀수를 판별하는 외부함수 정의하기

def is_even(num) :
    return num % 2 == 0

 

3. 이터레이터 클래스 생성하기

- 함수 이름을 그대로 변수로 넣으면 함수 자체를 실행

even_iter = EvenNumberIterator(1, 10, is_even)

### 이터레이터 반복 수행하기
for v in even_iter :
    print(v)

 텍스트 파일의 내용을 한줄씩 반환하는 이터레이터 만들기

1. 이터레이터 클래스 생성하기

- 파일명은 클래스가 받아서 처리

- readline() : 파일 정보중에 줄 하나 가지고 오는 함수

          >> 최초 이후부터는 다음 줄이 있는지 자동으로 체크 후 가지고 온다

          >> 다음 줄이 없으면 안가지고 온다.

class FileLineIterator :
    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):
        print("#1 ---------------------")
        
        # 파일에서 한 줄 읽어오기
        line = self.file.readline()
        
        # 읽어온 줄이 있으면 반환하고, 없으면 종료
        if line:
        	# strip은 문자열의 양 끝에서 공백 문자(공백, 탭, 개행문자 등)을 제거하는 역할
            return line.strip()
            
        else :
        	# 파일 닫기와 StopIteration 예외 발생
            print("#4 : 이터레이터 종료")
            self.file.close()
            raise StopIteration

 

 

2. 이터레이터 클래스 생성하기

file_path = "./04_example.txt"
file_iter = FileLineIterator(file_path)

 

3. 반복해서 파일 내용 한줄씩 출력하기

for line in file_iter :
    print(line)

 

- 메모장 내용과 주피터 노트북 실행 결과

# 결과
#1 ---------------------
가가가
#1 ---------------------
나나나
#1 ---------------------
다다다
#1 ---------------------
라라라
#1 ---------------------
#4 : 이터레이터 종료

 

 

 

728x90
반응형

+ Recent posts