git 이나 여러 레퍼런스에서 머신러닝 관련 소스코드를 참고하다 보면
generator 패턴을 종종 볼 수 있다.
대충 어떤 패턴인지는 인지하고 있지만, 좀 더 자세하게 알아보고자 정리를 해본다.
우선 제너레이터 패턴을 포함하는 이터레이터부터 살펴보자
Iterator(이터레이터)
keyword : collection type, sequence type, iterable, iterator
우선 진행하기 앞서 컬렉션 타입과 시퀀스 타입을 살펴보자
컬렉션 타입
- list, tuple, set, dictionary와 같이 여러 개의 요소(객체)를 갖는 데이터 타입
시퀀스 타입
- list, tuple, range, str등과 같이 순서가 존재하는 데이터 타입
컬렉션 타입, 시퀀스 타입을 이해했으면 다음 단계로 진행해보자
iterable 이란?
위 두 타입처럼 반복 가능한 객체에 적용되는 의미이며,
쉽게 말해 반복문의 사용 시 자료구조 내에 포함되어 있는 원소 하나하나에 접근할 수 있는 객체를 뜻한다.
보통 __iter__() 나 __getitem__() 메서드로 정의된 class는 모두 iterable 하다고 할 수 있다.
그럼 iterator란?
Iterator 는 next() 메서드로 데이터를 순차적으로 호출 가능한 object 이다.
만약 next() 로 다음 데이터를 불러올 수 없을 경우 (가장 마지막 데이터인 경우) StopIteration exception을 발생시킨다.
대충 보면 iterable, iterator란 모두 순차적으로 원소에 접근 호출하는 의미로 보인다.
그럼 iterable 한 객체들은 iterator인가?
결론부터 말하자면 iterable 하다고 해서 반드시 iterator 는 아니다.
앞서 말했다시피 list는 iterable 하지만, next() 메서드로 호출할 시 아래와 같이 에러가 발생한다.
sample = [1,2,3]
sample.__next__()
---------------------------------------------------------
AttributeError: 'list' object has no attribute '__next__'
즉 __next__ 메소드로 다음 값을 반환할 수 있으면 Iterator, 없으면 Iterable 한 객체이다.
하지만 이러한 iterable 한 객체에 iter()라는 built-in function을 사용하면 iterator 객체를 만들 수 있다.
sample = [1,2,3]
print(type(x))
y = iter(x)
print(type(y))
--------------------------------------------
<class 'list'>
<list_iterator object at 0x000001A3CB893208>
위와 같이 iter() 함수를 사용하여 list를 list_iterator 타입으로 변경 가능하다.
이제 iterator객체가 되었으니 아래와 같이 __next__ 메서드를 사용해 출력을 할 수 있다.
print(y.__next__())
print(y.__next__())
---------------------------------------------
1
2
그렇다면 iterator는 iterable의 정확한 차이는?
음.. 차이를 정확히 말하자면 Iterator는 Iterable의 자식 클래스이고,
Iterable을 상속받은 거라 말할 수 있기 때문에 차이라고 말할 수는 없지만 굳이 말하자면...
필자가 이해한 기준으로 설명을 해본다 (틀린 부분이 있다면 댓글 달아주세요)
Iterator의 개념적 설명은
상태를 유지하며 반환할 수 있는 마지막 값까지 원소를 필요할 때마다 하나씩 반환하는 것이다.
마지막 값까지 원소를 필요할 때마다 하나씩 반환하는 것이라는 부분에서는
iterable과 같지만
여기서 상태를 유지한다는 부분에 있어 차이가 있다.
앞서 sample 이라는 list를 보자
이 리스트는 iterable 객체이기 때문에 __next__ 메서드를 사용할 수 없다.
그렇다면 for 문을 통해 iterator 객체로 변환하여 출력해야 하는데
아래의 코드로 출력을 하게 되면
x = [1,2,3]
print(type(x))
for i in x:
print(i)
--------------------------
<class 'list'>
1
2
3
x의 모든 원소를 반환하게 된다.
하지만 이터레이터를 사용하면
x = [1,2,3]
y = iter(x)
print(type(y))
print(y.__next__())
print(y.__next__())
print('>>>> for문 시작')
for i in y:
print(i)
---------------------------
<class 'list_iterator'>
1
2
>>>> for문 시작
3
y 라는 이터레이터 객체는 위와 같이 __next__ 메서드를 사용하여 2까지 반환했던 상태를 유지하여
다시 for 문을 진행하였을 때 3 부터 반환하는 것을 볼 수 있다.
이러한 부분에 있어 차이가 있다고 말할 수 있지 않을까...라고 생각한다.
+ 추가
모든 각각의 iterator는 상태를 갖는다.
이 말에 대해 조금 더 추가해보고자 한다.
x =[1,2,3]
assert iter(x) != iter(x)
위 구문을 실행해보면 AssertionError 가 발생하지 않고 이는 곧
Iterable에 iter 함수를 쓸 때마다 새로운 이터레이터가 생성되며,
이때 각 Iterator는 서로 다른 상태를 유지하고 있다를 의미한다.
다시 말해 한 Iterator의 동작이 다른 Iterator의 동작에 영향을 미치지 않는다.
정리
이터러블(Iterable)
- __iter__
이터레이터(iterator)
- __iter__
- __next__
Iterator는 Iterable에 iter 내장 함수를 적용해 반환되는 객체이며
__next__ 함수를 통해 값을 한 번에 한 번씩 반환할 수 있으며
이때 각 iterator 객체는 상태를 유지한다.
마지막까지 반환하면 더 사용할 수 없으며 StopIteration 예외를 일으킨다.
🎥고돌한고돌이 youtube
'coding 📟 > python 💻' 카테고리의 다른 글
[Python] 가상환경 pyenv (1) | 2022.05.13 |
---|---|
[Python] 파이썬 가상환경이란? (1) | 2022.05.13 |
[Python] 리스트 원소 랜덤 추출 (0) | 2022.03.08 |
[Python] assert raise 비교 (0) | 2022.03.08 |
[Python] 파이썬 리스트, 배열 최대값 최소/최대 값 찾기 - min/max 함수 (0) | 2022.03.08 |
댓글