Iterable 객체 - 반복 가능한 객체
대표적으로 iterable한 타입 : list, dict, set, str, bytes, tuple, range
iterable한 타입을 확인하는 방법
collections.iterable
에 속한 instance인지 확인 : isinstance 함수는 첫 번째 파라미터, 두 번째 파라미터 클래스의 instance이면 True 반환한다.
import collections.abc
var_list = [1,3,4,5]
print(isinstance(var_list, collections.Iterable))
iterator 객체 : 하나 이상의 항목이 포함되어 있는 자료구조에서 데이터를 차례대로 꺼낼 수 있는 객체
iterator는 iterable한 객체를 내장함수 또는 iterable 객체의 메소드로 객체를 생성할 수 있다.
(반복 불가능 자료형, 자료형 변환 불가능)
iterator 객체는 next 내장 함수를 사용할 때마다 첫 번째, 두 번째, 세 번째 값이 출력된다.
a = [1,2,3]
a_iter = iter(a)
for _ in range(0,3):
print(next(a_iter), end = ' ')
# 결과
1 2 3
iterator 매직 메소드 __next__
를 통해 하나씩 값을 꺼냄 (마지막 원소 반환 후에는 StopIteration을 반환한다.)
b = {1,2,3}
b_iter = b.__iter__()
# b_iter.__next__()
for _ in range(0,3):
print(b_iter.__next__(), end = ' ')
# 결과
1 2 3
리스트, 튜플은 iterator로 변환 가능, 정수 값은 불가능
Iterable 객체와 Iterator 객체
파이썬의 어떤 객체가 __iter__
메소드를 포함하고 있다면 해당 객체를 Iterable 객체라고 부른다.
iter라는 내장 함수를 호출하면 내부적으로 해당 객체의 __iter__
메소드를 호출하게 된다.
__iter__
메소드는 Iterator 객체를 반환해주는데, Iterator 객체는 __next__
메소드를 반드시 구현하고 있어야 한다.
1) Iterable
__iter__
메소드 사용시 : 내부적으로 iterator 객체를 리턴한다.__iter__
추상메소드를 실제로 구현해야 하며 이 메소드는 호출될 때마다 새로운 Iterator를 반환해야 한다.2) Iterator
__next__
메소드 사용시 : next 내장 함수에 대해서 동작한다. 따라서 iterator 객체이다.__iter__
를 구현하되 자기 자신을 반환해야 한다.
Iterator 객체 만들기
1) __iter__
메소드 구현 : __iter__
메소드는 Iterator 객체를 리턴해야 하는데 Season 클래스 자기 자신이 Iterator 객체(__next__
를 포함한다.)이므로 자기 자신(self)를 리턴하도록 구현한다.
2) __next__
메소드 구현 : for 문에서 각 반복마다 계절의 이름이 순서대로 출력되는 기능을 __next__
메소드에 구현한다.
class Season :
def __init__(self):
self.data = ["봄","여름","가을","겨울"]
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.data):
cur_season = self.data[self.index]
self.index += 1
return cur_season
else:
raise StopIteration
3) Season 클래스의 객체를 생성한 후 iter 내장함수를 호출하여 Iterator 객체를 생성한다.
다음, Iterator 객체에 대해서 다시 next 내장 함수를 호출하여 반복적으로 값을 얻는다.
s = Season()
ir = iter(s)
print(next(ir))
print(next(ir))
print(next(ir))
print(next(ir))
OddCounter 클래스의 정의와 객체 생성
class OddCounter:
# 1부터 증가하는 홀수를 반환하는 클래스
def __init__(self, n = 1): # 초기화 메소드, n를 1로 둔다.
self.n = n
def __iter__(self): # 반복자 __iter__() 함수를 가져야 한다.
return self
def __next__(self): # 반복자는 __next__() 함수를 가져야 한다.
t = self.n # self.n을 임시 변수 t에 지정해 두고
self.n += 2 # self.n을 2씩 증가
return t # t부터 출력해야 1이 가장 먼저 출력된다.
my_counter = OddCounter()
for x in my_counter:
if x > 20: # 반복을 종료하는 조건 : x > 20
break
print(x, end= ' ')
# 결과
1 3 5 7 9 11 13 15 17 19
OddCounter 클래스와 StopIteration 예외 생성 가능
class OddCounter:
# 1부터 증가하는 홀수를 반환하는 클래스
def __init__(self, n = 1): # 초기화 메소드, n를 1로 둔다.
self.n = n
def __iter__(self): # 반복자 __iter__() 함수를 가져야 한다.
return self
def __next__(self): # 반복자는 __next__() 함수를 가져야 한다.
if self.n < 20:
t = self.n # self.n을 임시 변수 t에 지정해 두고
self.n += 2 # self.n을 2씩 증가
return t # t부터 출력해야 1이 가장 먼저 출력된다.
raise StopIteration # 조건을 만족하지 않으면 StopIteration을 raise한다.
my_counter = OddCounter()
for x in my_counter:
print(x, end= ' ')
# 결과
1 3 5 7 9 11 13 15 17 19
반복가능 객체를 위한 내장함수
파이썬의 반복기능 iterable 객체는 다양한 내장함수들을 적용할 수 있다.
l1 = [1,2,3,4]
l2 = [0,2,4,8]
l3 = [0,0,0,0]
print(all(l1)) # 모든 요소가 true(0이 아닌 값)일 때만 True를 반환한다.
# True
print(any(l3)) # 항목들 중에서 하나라도 true(0이 아닌 값)일 경우 True를 반환한다.
# False
ex) 잘못된 일처리 방식 : 빵이 100개 될 때까지 순서대로 하나씩 빵을 구은 후 100개가 준비되면 이를 점원에게 한 번에 전달
빵을 낱개로 포장하는 거라면 제빵사가 빵을 한 번에 100개를 전달하는 것이 아니라 2개나 5개 처럼 빵이 일부라도 준비가 될 때 전달해주는 것이다.
파이썬은 반복자 말고도 generator라는 객체를 제공하는데 이 객체는 모든 값을 메모리에 올려두고 이용하는 것이 아니라 필요할 때마다 생성해서 반환하는 일을 한다.
(메모리를 효율적으로 활용할 수 있다는 장점이 있다.)
my_generator = (x for x in range(1,4))
for n in my_generator:
print(n)
print(type(my_generator))
for 문에서 필요로 할 때마다 반환해주고 메모리에서 보관하지 않는다.
generator와 yield
yield 문 : 함수를 종결하지 않으면서 값을 계속 반환
generator 객체를 생성했다면 next()함수 호출을 통해 generator 객체 내의 코드를 실행할 수 있는 이때 yield 구문을 만나면 파이썬 함수의 return처럼 yield 키워드에 있는 값(그림에서 i)을 호출부로 리턴하고 실행의 흐름도 호출부로 이동한다.
generator 함수를 호출하면, generator 클래스의 객체가 생성된다.
def genFunc():
for i in range(3):
yield i
gen = genFunc()
print(gen,type(gen))
# 결과
<generator object genFunc at 0x000001E49B10FF90> <class 'generator'>
def genFunc():
for i in range(3):
yield i
gen = genFunc()
for i in gen:
print(i)
# 결과
0 1 2
for문을 사용하면 반복할 때마다 내부적으로 next(g)를 호출하는데 이는 StopIteration이 발생할 때까지 계속된다.
List, Set, Dictionary의 표현식의 내부도 generator이다.
yield from
yield문을 여러 번 바깥으로 전달하려면 for문을 사용해야 한다.
def three_generator():
a = [1, 2, 3]
for i in a:
yield i
gen = three_generator()
list(gen)
# 결과
[1, 2, 3]
for문 대신에 iterable한 객체를 yield할 때는 yield from iterable로 값을 전달할 수 있다.
def three_generator2():
a = [1, 2, 3]
yield from a
gen = three_generator2()
print(list(gen))
# 결과
[1, 2, 3]
iterator : 요소가 복수인 컨테이너(리스트, 튜플, 셋, 사전, 문자열)에서 각 요소를 하나씩 꺼내 어떤 처리를 수행할 수 있도록 하는 간편한 방법을 제공하는 객체
generator : iterator의 한 종류로, 하나의 요소를 꺼내려고 할 때마다 요소 generator를 수행하는 타입으로, Python에서는 yield문을 통해 구현하는 것이 보통이다.
python의 내장 컬렉션 (list, tuple, set, dic 등)의 어떤 것이든 iterable을 상속받고, 내장의 컬렉션을 사용한 반복문 처리로 미리 컬렉션 값을 입력해 둬야 할 필요가 있으므로 아래와 같은 경우는 interator 또는 generator을 직접 구현하고 싶다고 생각하는 경우가 있을 것이다.
iterator와 generator의 관계도
(cf) 각 자료구조의 iterable, Generator, Iterator의 상속 여부
언더 스코어('_')
1) __이름__
: 내장된 특수한 함수와 변수를 나타낸다.
2) 변수명_
: 예약어를 변수명으로 사용할 수 없을 때 사용한다.
3) _함수
: 한 모듈 내부에서만 사용한 private 클래스/함수/변수/메서드를 선언할 때 사용하는 컨벤션
참고 자료