파이썬

메모리 절약 - 이터레이터

초롱씨 2023. 11. 16. 16:10
728x90
반응형

 

메모리가 부족할 경우 이런 상황이 발생한다

이터레이터(Iterator)는 파이썬에서 순회 가능한 객체(iterable)에서 값을 하나씩 가져오는 객체.
이터레이터는 __iter__()와 __next__() 메서드를 구현한 객체로,
iter() 함수를 사용하여 이터레이터 객체를 생성하고,
next() 함수를 사용하여 다음 값을 가져올 수 있다.

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


### 클래스 정의하기
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_value의 값이 5보다 작을 때 까지 반복 수행
        if self.current_value <5:
            # - 반환할 변수에 current_value의 현재 값 지정
            result = self.current_value
            # - current_value의 현재 값을 1증가
            self.current_value +=1
            print(f'#4 result = {result}  /  self.current_value = {self.current_value}')

            # - result 값 반환
            return result
        else:
            print("#5 : StropIteration 예외 발생!!")
            ### 이터레이터는 반복이 끝나면 종료시켜야 함
            # - 종료시키는 방법은 강제로 오류 발생시킴
            raise StopIteration
### 이터레이터 실행시키기
# 클래스 생성하기
my_iterator = MyIterator()
#1 (__init__): self = <__main__.MyIterator object at 0x000001DDEF288B90> / self.current_value=0

 

### 이터레이터 기능은 반복문(for or while)을 사용해야만 작동하는 기능함
# - 최초 __iter__() 함수를 호출하고
# - 출력시 __next__() 함수가 한번씩 수행하면서 값을 반환받아서 출력함
# - 한번 반환된 후 메모리는 초기화되며 다음 반복시 다시 메모리 사용
# ** 메모리를 효율적으로 활용할 수 있음ㄲ

#반복 수행하여 result값 출력하기
for value in my_iterator:
    print (value)

#2 (__iter__) : self = <__main__.MyIterator object at 0x000001DDEF06A4D0>
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF06A4D0>
#4 result = 0  /  self.current_value = 1
0
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF06A4D0>
#4 result = 1  /  self.current_value = 2
1
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF06A4D0>
#4 result = 2  /  self.current_value = 3
2
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF06A4D0>
#4 result = 3  /  self.current_value = 4
3
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF06A4D0>
#4 result = 4  /  self.current_value = 5
4
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF06A4D0>
#5 : StropIteration 예외 발생!!

 

### 이터레이터 실행시키기
# 클래스 생성하기
my_iterator = MyIterator()

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

 

try:
    print (next(my_iterator))
    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 0x000001DDEF816410>
#4 result = 0  /  self.current_value = 1
0
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF816410>
#4 result = 1  /  self.current_value = 2
1
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF816410>
#4 result = 2  /  self.current_value = 3
2
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF816410>
#4 result = 3  /  self.current_value = 4
3
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF816410>
#4 result = 4  /  self.current_value = 5
4
#3 (__next__) : self = <__main__.MyIterator object at 0x000001DDEF816410>
#5 : StropIteration 예외 발생!!
이터레이터가 종료되었습니다.

 

print (iter(my_iterator))

#2 (__iter__) : self = <__main__.MyIterator object at 0x000001DDEF816410>
<__main__.MyIterator object at 0x000001DDEF816410>

 

for i in range(len("hello")):
    print("hello"[i])

h
e
l
l
o

 

# <예제 문자열을 전달 받아서 문자 하나씩 추출하여 반환하는 이터레이터 생성>
### 문자열을 "Hello"의 각 문자 하나씩 출력하는 프로그램을 작성해주세요
# 임의의 문자열을 받아서 처리
# 임의의 문자열의 외부에서 클래스 생성시 넣어줍니다.
class StringIterator:
    def __init__(self, x):
        self.my_string = x
        self.index = 0
    def __iter__(self):
        return self
    def __next__(self):
        if self.index <len(self.my_string):
            chr = self.my_string[self.index]
            self.index +=1
            return chr
        else:
            print("출력종료")
            raise StopIteration      
### 이터레이터 기능 사용하기 
# 반복문을 이용해서 전체 반복하기
# 클래스 생성하
string_iterator = StringIterator(input("출력할 문자를 입력하세요"))
for value in string_iterator:
    print(value)

출력할 문자를 입력하세요 파이썬



출력종료

 

my_iterator = iter(input("출력할 작성"))

# 이터레이터 사용
for value in my_iterator:
    print(value)

출력할 작성 파이썬


 

from memory_profiler import profile

 

### 주피터노트북에서는 아래 로드 처리 해야함
%load_ext memory_profiler

 

 

### 이터레이터 클래스 생성하기
class SimplIterator :
    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 = SimplIterator(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_13156\1496161889.py
peak memory: 128.84 MiB, increment: 27.24 MiB
ERROR: Could not find file C:\Users\user\AppData\Local\Temp\ipykernel_13156\1496161889.py
peak memory: 104.16 MiB, increment: 0.00 MiB

 

일반 반복문의 경우 128, 반복시 마다 메모리가 27만큼 추가사용

이터레이터 사용의 경우 104!!  반복시에는 메모리가 추가사용되지 않는다.

 

 

 

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

 

### 이터레이터 클래스 : EvenNumberIterator

class EvenNumberIterator:
    def __init__(self,start,end):
        self.start = start
        self.end = end
    def __iter__(self):
        return self
    def __next__(self):
        # for x in range(self.start,self.end+1):
        #     if self.start % 2 == 0:
        #         result = self.start
        #         self.start += 1
        #         return result
        #     else:
        #         self.start += 1
        # raise StopIteration


        # if self.start <= self.end:
        if self.start <= self.end:
            if self.start % 2 == 0 :
                result = self.start
                self.start += 1
                return result
            else:
                self.start += 1
                return self.__next__()
        else:
            raise StopIteration
startnum = 1
endnum = 10
even_iter = EvenNumberIterator(startnum,endnum)

 

for v in even_iter:
    print(v)

2
4
6
8
10

try:
    print (next(even_iter))
    print (next(even_iter))
    print (next(even_iter))
    print (next(even_iter))
    print (next(even_iter))
    print (next(even_iter))

    print (next(even_iter))
except:
    print ("이터레이터가 종료되었습니다.")
    

2
4
6
8
10
이터레이터가 종료되었습니다.

 

# < 예제 외부 함수를 이용해서 짝수값 추출하는 이터레이터 만들기>
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        
            
### 짝수와 홀수를 판별하는 외부함수 정의하기
def is_even(num):
    return num%2 == 0
### 이터레이터 클래스 생성하기
even_iter = EvenNumberIterator(1,10,is_even)

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

2
4
6
8
10

 

<예제> 텍스트 파일의 내용을 한줄씩 반환하는 이터레이터 만들기
### 클래스 이름 : FileLineIterator
# 파일명은 외부에서 클래스에 주면된다.
class FileLineIterator:
    def __init__(self, file_name):
        self.file_name= file_name
        self.file = open(self.file_name,'r', encoding="utf-8")
    def __iter__(self):
        return self

    def __next__(self):
        result = self.file.readline()
        print(f"#2 : line = {result}")

        if result:
            print(f"#3 : line = {result}")
            return result
        else:
            print(f"#4 : 이터레이터 종료...")
            self.file.close()
            raise StopIteration
lead = FileLineIterator("04_example.txt")
lead2 = FileLineIterator("05_example.txt")
for value in lead:
    print(value)

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⡴⠒⠚⣻⠇⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠓⠒⠒⠒⠒⢤⣤⠴⠚⠉⠀⡸⠁⣠⠞⠁⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡆⠀⠀⣠⠖⠋⠀⠀⠀⠀⢀⡧⠞⠣⠤⣀⡀⠀⠀⠀⠀

⢀⣤⠔⠒⠚⣏⠉⠉⠉⠉⠉⠉⠉⠒⠒⠲⠤⠒⠋⠉⠉⠉⠉⠉⠒⠒⠻⢴⠋⠀⠀⠀⠀⠀⣠⠔⠋⠀⠀⠀⠀⠀⠉⠑⠲⢤⡀

⠈⠙⠒⠤⢄⣘⣦⡀⠀⠀⠀⠀⠀⠀⡔⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣤⠤⠖⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼

⠀⠀⠀⠀⠀⠀⠈⢉⣿⣗⡒⠒⠒⡾⠁⣠⣶⠒⡆⠀⠀⠀⠀⠀⠀⠀⣀⣄⡀⠀⢳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠞⠀

⠀⠀⠀⠀⠀⠀⢠⡎⠀⠀⠙⢦⣀⠇⠀⠻⣼⡿⠁⠀⠀⢠⡄⠀⠀⠸⣷⣼⣷⠀⢸⣆⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⠋⠀⠀

⠀⠀⠀⠀⠀⠀⠈⣏⠀⠀⠀⠀⡿⠖⠲⣄⠀⠀⣤⡀⢀⣤⣀⠀⠀⢀⠈⠋⠁⠀⢸⣿⡉⠓⠦⣀⡀⠀⠀⠀⠀⢀⡴⠁⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⢹⡀⠀⠀⠀⡇⠀⠀⣸⠀⠀⢸⣯⠟⠛⠛⢿⣿⠋⠀⢰⠟⠉⠹⡇⢷⠀⠀⠀⠉⠓⠦⣄⣠⠎⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⣇⠀⠀⠀⠹⡦⠴⠋⠀⠀⠀⢹⡄⠀⢀⡼⠁⠀⠀⣇⠀⠀⢠⡇⣀⣧⠀⠀⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠸⡄⠀⠀⠀⠙⢆⠀⠀⠀⠀⠀⠹⠤⠋⠀⠀⠀⠀⠈⠓⡶⠋⠙⠳⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⡄⠀⠀⠀⠀⠑⠶⠀⠀⠀⠀⠀⠀⠀⠀⠀⣤⠖⠋⠀⠀⠀⠀⠀⠀⠉⠲⢤⡀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣶⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡀⠀⠀⠀⠀⠀⠀⢀⣷⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣧⠤⣤⠤⠴⠒⠒⠚⠁⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢧⡰⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡸⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⡆⢀⣠⠤⠒⠒⠒⠂⠀⠀⠐⠒⠒⠒⠒⠲⢦⡀⠀⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⣿⡟⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠒⠾⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

 

for value in lead2:
    print(value)

⠀  ⠀⠀⠀⠀⠀       ⠀⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⢀⣿⣿⡿⠟⠙⠛⠛⠁⠀⠈⠙⢿⣿⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⢸⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⣼⣿⣿⠀⠀⠊⣉⡙⠆⣤⣞⡉⠃⢸⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀

⠀⠀⠀⠀⠀⣻⣿⣿⡆⠀⠀⠈⢁⣠⣈⡛⠁⠀⢸⣿⡟⠀⠀⢀⢀⠀⠀⠀

⠀ ⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠰⣯⣼⣿⣿⠀⠀⠀⢻⣿⣦⡀⡏⠉⠑⡄

⠀⠀⠀⣾⣿⣿⣿⡇⠀⠀⠀⠀⠙⠿⠿⠋⠀⠀⠀⠈⣿⣿⡃⢱⠀⠀⡇⠀⠀

⠀⠀⠀⢸⣿⣿⣿⣷⠤⣄⣀⣀⣀⠀⠀⠀⠀⢀⡠⠾⠿⠟⢡⠃⠀⠀⡇⠀⠀

⠀⠀⠀⠈⠙⠓⠀⠀⠀⣴⣿⣧⣼⣽⣷⣾⣟⠉⠉⠒⠒⠊⠀⠀⠀⡇⠀⠀⠀

⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣧⡀⠀⠀⠀⠀⣠⠎⠀⠀⠀⠀

⠀⠀⠀⠀⠀⠀⢀⠜⣽⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣀⡤⠖⠋⠀⠀

 

그런데 한줄씩 읽은 값이 공백이 생기면서 그림 모양이 깨지는 현상이 발생합니다.

그래서 공백을 제거하는 strip()함수를 사용해줍니다.

 

return result.strip()

728x90
반응형