이터레이터 (iterator)

19. 이터레이터 (iterator)

  • 몇몇 특수한 데이터 집합은 내부의 각 데이터로 분리해서 처리할 수 있음
  • 예:
    • list, set, dictionary 등의 컬렉션(collection)
    • 문자열: 문자열을 이루는 각 문자 Sequence
  • 이와 같은 컬렉션(collection), Sequence등을 iterable 객체(iterable Object)라고 함
  • 간단히 for 구문으로 각 데이터를 탐색할 수 있는 데이터 집합
# 예1: 리스트 컬렉션
for num in [1, 2, 3, 4, 5]:
    print(num)
# 예2: 문자 Sequence
for char in "Dave Lee":
    print(char)

iterable 과 iterator

  • iterable 객체: itertator를 리턴할 수 있는 객체
    • 이터레이터를 리턴할 수 있는 객체
  • iterator
    • 순차적으로 다음 데이터를 리턴할 수 있는 객체
    • Iterator는 내장 함수 next 함수를 사용해서, 순환하는 다음 값을 반환함

내장 함수 iter()

  • iterator 객체를 생성할 수 있음
my_list = [1, 2, 3, 4, 5]
print(next(my_list))
my_list_iterator = iter(my_list)
print(next(my_list_iterator))

내장 함수 next()

  • Iterator의 다음 값, 즉 순환하는 값을 반환
iterator_my_list = iter(my_list)
next(iterator_my_list)
next(iterator_my_list)
next(iterator_my_list)
next(iterator_my_list)
next(iterator_my_list)
# 더이상 순회할 데이터가 없으면, StopIteration 발생
next(iterator_my_list)

for 구문과 iterator

  • for 구문 사용시, 파이썬은 매번 next 함수로 iterator의 다음 값을 읽어내는 것임
  • StopIteration 발생할 때까지 next 함수를 호출한다고 보면 됨

Custom 이터레이터 만들기

  • 직접 만들 수 있음
    • iterable 객체는 iter 메서드를 가지고 있는 클래스
    • iterator 객체는 next 메서드를 가지고 있는 클래스

iterable 객체와 iterator 클래스 만들기

class Counter:
    def __init__(self, stop):
        self.stop = stop    # 반복을 끝낼 숫자
 
    def __iter__(self):                      # <--- iterable 객체는 __iter__ 메서드가 존재함
        return Counter_Iterator(self.stop)   # <--- Counter 의 iterator 객체를 리턴해줌

class Counter_Iterator:
    def __init__(self, stop):
        self.current = 0    # 현재 상태를 확인하기 위한 속성
        self.stop = stop    

    def __next__(self):                 # <--- iterator는 __next__ 메서드가 존재함
        if self.current < self.stop:    # 현재 상태가 stop보다 적을 때는 현재 상태값을 리턴해주고, 1씩 상태값을 증가시킴
            return_value = self.current            
            self.current += 1           
            return return_value         
        else:                           # 현재 상태가 stop과 동일 또는 클 때는 StopIteration 이벤트를 발생시킴 
            raise StopIteration         # 예외 발생
counter_iterator = iter(Counter(5))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
for num in Counter(3):
    print (num)

iterable 객체이자 iterator로 한번에 클래스 만들기

class Counter:
    def __init__(self, stop):
        self.current = 0    # 현재 상태를 확인하기 위한 속성
        self.stop = stop    # 반복을 끝낼 숫자
 
    def __iter__(self):                      # <--- iterable 객체는 __iter__ 메서드가 존재함
        return self   # <--- Counter 의 iterator 객체를 리턴해줌

    def __next__(self):                 # <--- iterator는 __next__ 메서드가 존재함
        if self.current < self.stop:    # 현재 상태가 stop보다 적을 때는 현재 상태값을 리턴해주고, 1씩 상태값을 증가시킴
            return_value = self.current            
            self.current += 1           
            return return_value         
        else:                           # 현재 상태가 stop과 동일 또는 클 때는 StopIteration 이벤트를 발생시킴 
            raise StopIteration         # 예외 발생
counter_iterator = iter(Counter(5))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
# iterable 객체이자 iterator임
print (next(Counter(2)))
for num in Counter(3):
    print (num)
def iter(object):
    return object.__iter__()

def next(object):
    return object.__next__()

for num in range(10):
    
class Counter:
    def __init__(self, stop):
        self.stop = stop    # 반복을 끝낼 숫자
 
    def __iter__(self):                      # <--- iterable 객체는 __iter__ 메서드가 존재함
        return Counter_Iterator(self.stop)   # <--- Counter 의 iterator 객체를 리턴해줌

class Counter_Iterator:
    def __init__(self, stop):
        self.current = stop    # 현재 상태를 확인하기 위한 속성
        self.stop = stop    

    def __next__(self):                 # <--- iterator는 __next__ 메서드가 존재함
        if self.current > 0:    # 현재 상태가 stop보다 적을 때는 현재 상태값을 리턴해주고, 1씩 상태값을 증가시킴
            return_value = self.current            
            self.current -= 1           
            return return_value         
        else:                           # 현재 상태가 stop과 동일 또는 클 때는 StopIteration 이벤트를 발생시킴 
            raise StopIteration         # 예외 발생
counter_iterator = iter(Counter(5))
print(next(counter_iterator))
print(next(counter_iterator))
print(next(counter_iterator))
print(next(counter_iterator))
print(next(counter_iterator))
초간단 연습1
- 위 Counter 클래스는 인자로 넣어지는 수까지를 0부터 순차적으로 1씩 증가된 값을 리턴해주는 클래스입니다.
- 이번에는 인자로 넣어지는 수부터 시작해서 0까지 순차적으로 1씩 감소된 값을 리턴해주는 클래스를 만들어보세요
class Counter:
    def __init__(self, stop):
        self.stop = stop    # 반복을 끝낼 숫자
 
    def __iter__(self):                      # <--- iterable 객체는 __iter__ 메서드가 존재함
        return Counter_Iterator(self.stop)   # <--- Counter 의 iterator 객체를 리턴해줌

class Counter_Iterator:
    def __init__(self, stop):
        self.current = stop    # 현재 상태를 확인하기 위한 속성

    def __next__(self):                 # <--- iterator는 __next__ 메서드가 존재함
        if self.current > 0:    # 현재 상태가 stop보다 적을 때는 현재 상태값을 리턴해주고, 1씩 상태값을 증가시킴
            return_value = self.current            
            self.current -= 1           
            return return_value         
        else:                           # 현재 상태가 stop과 동일 또는 클 때는 StopIteration 이벤트를 발생시킴 
            raise StopIteration         # 예외 발생

counter_iterator = iter(Counter(5))
print (next(counter_iterator))
print (next(counter_iterator))
협업 과제1(남에게 설명하면, 자신이 배운답니다.!)
- 특정 수와 배수를 입력받아 특정 수의 배수를 특정 수까지 리턴하는 이터레이터 만들기
예: 20, 2 를 입력받으면 0, 2, 4, ~~~ 20까지 리턴
class Counter:
    def __init__(self, stop, multiple):
        self.current = 0    # 현재 상태를 확인하기 위한 속성
        self.stop = stop    # 반복을 끝낼 숫자
        self.multiple = multiple
 
    def __iter__(self):                      # <--- iterable 객체는 __iter__ 메서드가 존재함
        return self   # <--- Counter 의 iterator 객체를 리턴해줌

    def __next__(self):                 # <--- iterator는 __next__ 메서드가 존재함
        if self.current < self.stop:    # 현재 상태가 stop보다 적을 때는 현재 상태값을 리턴해주고, 1씩 상태값을 증가시킴
            return_value = self.current            
            self.current = self.current + self.multiple           
            return return_value         
        else:                           # 현재 상태가 stop과 동일 또는 클 때는 StopIteration 이벤트를 발생시킴 
            raise StopIteration         # 예외 발생

counter_iterator = iter(Counter(20, 2))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))
print (next(counter_iterator))

협업 과제2(남에게 설명하면, 자신이 배운답니다.!)
다음 dataset은 이름을 가지고 있습니다. (타이타닉호 승선자 명단)
스트링에서 Mr. Miss, Mrs. 정보를 추출해서 각각 (Male), (Female) 출력해주는 데코레이터를 작성하세요
해당 데코레이터를 활용해서 이름 정보 리스트 변수를 넣어, 이름을 추력하는 함수를 작성해보세요
dataset과 이름에서 Mr. Miss. Mrs. 정보를 추출하는 코드는 다음 코드를 참고하세요

dataset = ['Braund, Mr. Owen Harris',
'Cumings, Mrs. John Bradley (Florence Briggs Thayer)',
'Heikkinen, Miss. Laina',
'Futrelle, Mrs. Jacques Heath (Lily May Peel)',
'Allen, Mr. William Henry',
'Moran, Mr. James',
'McCarthy, Mr. Timothy J',
'Palsson, Master. Gosta Leonard',
'Johnson, Mrs. Oscar W (Elisabeth Vilhelmina Berg)',
'Nasser, Mrs. Nicholas (Adele Achem)',
'Sandstrom, Miss. Marguerite Rut',
'Bonnell, Miss. Elizabeth',
'Saundercock, Mr. William Henry',
'Andersson, Mr. Anders Johan',
'Vestrom, Miss. Hulda Amanda Adolfina',
'Hewlett, Mrs. (Mary D Kingcome) ',
'Rice, Master. Eugene',
'Williams, Mr. Charles Eugene',
'Vander Planke, Mrs. Julius (Emelia Maria Vandemoortele)',
'Masselmani, Mrs. Fatima',
'Fynney, Mr. Joseph J',
'Beesley, Mr. Lawrence',
'McGowan, Miss. Anna "Annie"',
'Sloper, Mr. William Thompson',
'Palsson, Miss. Torborg Danira',
'Asplund, Mrs. Carl Oscar (Selma Augusta Emilia Johansson)',
'Emir, Mr. Farred Chehab',
'Fortune, Mr. Charles Alexander',
'Dwyer, Miss. Ellen "Nellie"',
'Todoroff, Mr. Lalio']
import re
p = re.compile('([A-Za-z]+)\.')
for name in dataset:
    matched = p.search(name)
    print(matched.group())

이름에서 앞에 이름만 빼고 싶은 분들이 있었음

import re
for name in dataset:
    names = name.split(' ')
    print (names[0])
참고
다음 코드는 openpyxl 라이브러리로 엑셀파일에서 이름 데이터를 읽어내는 코드입니다.
실제로 대량의 데이터를 읽어내는 코드입니다.
간단한 문제: F 로 시작하는 이름만 2번째 열에 F 로 표시하기
import openpyxl
 
# 엑셀파일 열기
wb = openpyxl.load_workbook('data/name.xlsx')
 
# 현재 Active Sheet 얻기
ws = wb.active
# ws = wb.get_sheet_by_name("Sheet1")

for r in ws.rows:
    name = r[0].value
    if name[0] == 'F':
        ws.cell(row=r[0].row, column=2).value = 'F'
    # ws.cell(row=row_index, column=5).value = sum
 
    print(name)
 
# 엑셀 파일 저장
wb.save("score2.xlsx")
wb.close()

협업과제2를 실제 엑셀파일까지 연계해서 풀어보기

import openpyxl
import re
# 엑셀파일 열기
wb = openpyxl.load_workbook('data/name.xlsx')
 
# 현재 Active Sheet 얻기
ws = wb.active
# ws = wb.get_sheet_by_name("Sheet1")

for r in ws.rows:
    name = r[0].value
    p = re.compile('([A-Za-z]+)\.')
    if p.search(name):
        matched = p.search(name)
        if matched.group() == 'Mr.' or matched.group() == 'Master.':
            ws.cell(row=r[0].row, column=2).value = '(Male)'
        elif matched.group() == 'Mrs.' or matched.group() == 'Miss.' or matched.group() == 'Ms.':
            ws.cell(row=r[0].row, column=2).value = '(Female)'
        else:
            ws.cell(row=r[0].row, column=2).value = '(Unknown)'
 
# 엑셀 파일 저장
wb.save("score2.xlsx")
wb.close()