Iterable and Iteartor, and Generator
Iterable
for
문에서in
뒤에 위치하여 iterate (반복, 순회)가 가능한 object를 가르킴.__iter__()
라는 special method를 구현하고 있으며, 이를 통해 자신에 대한iterator
object를 반환할 수 있음.__iter__()
special method는- 보통
iter()
함수에 argument로 "iterable
instance"가 전달되는 경우 - 해당 argument로 넘겨진 객체(=iterable 객체)의
__iter__()
메서드 가 호출되어 - 반환되는
iterator
객체가iter()
함수에서 최종 반환됨.
- 보통
- 여러 데이터를 묶어서 관리하는
Collection
들의 object들의 경우, 거의 다iterable
object라고 봐도 큰 문제 없다.
collection.abc
의 Iterable
abstract base class로 abstraction 되어 있음.
iterate는 반복 또는 순회라고 번역되며,
python 에서는
collection 내의 각 item 들을 처음부터 끝까지 하나씩 처리하는 것을 의미한다.
데이터들을 묶어서 관리하는 데 사용되는 list
, tuple
, dictionary
, set
과 string
, 심지어 file에 접근하는 wrapper object 모두 collection.Iterable
의 subclass 이며,for
문을 통해 iteration이 가능하다.
from collections.abc import Iterable
cs = (list, tuple, set, dict)
for a in cs:
print(f'{a.__name__} is a subclass of Itearble', issubclass(a, Iterable))
Iterable
는
자신을 iterate하기 위해 사용되는 일종의 view에 해당하는 Iterator
를 생성 및 반환할 수 있다.
이 때, Iterator
객체를 얻기 위해 사용되는 함수가 iter()
함수이고
실제적으로는 Iterable
object의 __iter__()
special method가 사용됨.
만약 custom iterable object를 만든다면,
def class
를 통해 클래스를 만들면서,
자신에 대한Iterator
를 반환하는__iter__(self)
special method를 구현하면 된다.
Iterator
- built-in function
iter()
에 iteration의 대상이 될Iterable
Object를 넘겨주거나,
해당Iterable
Object의__iter__()
special method로 얻어짐. - 자신의 special method
__next__()
를 통해 "가지고 있는 elements에서의 iteration이 가능"함.
또는 built-in functionnext()
에 해당Iterator
의 object를 argument로 넘겨주는 방식으로도 사용가능
(이 방법도 내부적으로는__next__()
를 사용함.)- 모든 element가
__next__()
를 통해 반환되고 나면,
이후 해당 method 호출 시StopIteration
Excpetion이 발생.
- 모든 element가
Iterable
Object와 마찬가지로for
문에서in
뒤에 놓여져서 iteration이 가능함.
for
문으로 살펴본 Iterable
과 Iterator
의 관계는 다음과 같음.
다음과 같이 for
문으로 iteration을 수행하면
for element in iterable_object:
print(element)
실제로는 아래와 같은 동작이 이루어진다고 생각하면 된다.
# iter() 통해 iterator 얻어냄
iterator_object = iter(iterable_object)
while True:
# next() 통해, Iterator에서 다음 element 를 얻어냄.
try:
element = next(iterator_object)
print(element)
# 다음 element 가 없을 경우 StopIteration Exception 발생
except StopIteration:
break
Custom Iterable and Iterator
다음과 같이 special methods들을 overriding하여 Custom Iterable
과 Iterator
를 만들 수 있음.
class DsIterable: #Iterable 이면서 Iterator임.
def __init__(self, src_seq):
self.src = src_seq
self._current = 0
def __iter__(self): # Iterable
return self # 자신에 대한 Iterator객체 반환
def __next__(self): # Iterator
current = self._current
self._current = self._current + 1
if self._current > len(self.src):
raise StopIteration
return self.src[current]
위에서 만든 Iterable
로 for
문을 사용하면 다음과 같음.
src = [1,2,3,4]
iter = DsIterable(src)
for i in iter:
print(i)
Generator
generator
는
주로 generator iterator
를 생성하는 function
를 가르키거나,
"generator iterator
자체" 를 지칭하기도 함.
yield
를 이용한 function으로 구현되거나,- collection에 대해
yield from
을 사용한 function으로 구현되거나 tuple
을 이용한 comprehension을 통해 generator expression을 이용.
A generator is something that you can iterate over (for us, usually using for)
but whose values are produced only as needed (lazily).
일반적으로 iterable
의 경우,
해당 collection에서 가지고 있는 모든 item을 memory에 할당하여 관리하는 방식을 사용하는데
이 경우 매우 많은 item을 가질 경우 iterable
object의 memory size가 커지게 됨.
이같은 단점을 해결하기 위해 제안된 generator iterator
는
동적으로 element를 요청받을 때 하나씩 생성하여 내보내는 iterator
로
아주 적은 크기의 item을 가지는 iterable
보다는 memory를 더 차지하지만,
많은 양의 item을 가지는 경우에는 보다 적은 크기의 memory만을 효율적으로 사용한다는 장점을 가짐.
- 필요할 때에 item을 생성하는 generator는
처음부터 다시 iteration을 하려면 generator를 재생성해야하는 단점이 있음. - 여러번 iterate를 처음부터 해야하는 경우엔, list 등의 iterable을 생성하는게 보다 효과적임..
generator의 대표적인 예로는 Built-in function인range()
를 들 수 있다.range
는 Python 3.x에서generator인 range object를
반환한다. (Python 2.x 에서는 실제로 모든 item을 memory에 적재한list
를 반환함)
참고로, generator
는 값을 반환해주는 return
대신에 yield
함수를 통해 현재 item을 반환해준다.
값을 반환하고 나서 종료가 되는 return과 달리
yield는 값을 반환해주지만,
해당 함수를 종료하지 않고 상태를 그대로 유지한다.
때문에 이후 해당 함수가 다시 호출되는 경우, 이전 상태에서 이어서 처리가 이루지는 동작특성을 가지며, 이를 통해 generator
를 구현할 수 있다.
yield를 이용한 function으로 구현
다음은 간단한 generator의 구현 예제로 0부터 generator iterator를 생성시 넘겨준 end
argument 값까지를 반환해주는 일종의 range
와 비슷한 object를 만드는 법을 보여줌.
def ds_get_generator(end):
for i in range(0, end+1):
yield i
g = ds_get_generator(3)
for c in g:
print(c)
위의 코드의 하단부의 for
문에서 0~3까지를 한 줄씩 출력되는 것을 확인할 수 있음.
sys.getsizeof(g)
를 통해 차지하고 있는 memory size를 비교해볼 수 있음.
이를 이용하여 5000개 정도의 숫자를 item으로 가지는iterable
과generator
를 비교해보라.
iterable 객체 전체를 사용하는 yield from
앞서 살펴본 경우, for
문으로 item 하나씩 yield
로 반환(or 전달)해주는 방식에
추가적으로 iterable
object에 대한 generator iterable을 생성하는 yield from
이 Python 3.3 부터 추가됨.
def ds_get_generator(end):
r = range(0, end+1)
l = list(r)
yield from l
g = ds_get_generator(3)
for c in g:
print(c)
generator expression 으로 구현
2023.06.06 - [Programming] - [Python] List Comprehension
'Programming' 카테고리의 다른 글
[Programming] Application Programming Interface (API) (0) | 2023.06.08 |
---|---|
[Python] range and enumerate (0) | 2023.06.07 |
[Python] List Comprehension (0) | 2023.06.06 |
[Python] Assignment와 Shallow Copy, Deep Copy (0) | 2023.06.05 |
[Programming] Garbage Collection (GC) (0) | 2023.06.05 |