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.abcIterable abstract base class로 abstraction 되어 있음.

iterate는 반복 또는 순회라고 번역되며,
python 에서는
collection 내의 각 item 들을 처음부터 끝까지 하나씩 처리하는 것을 의미한다.

 

데이터들을 묶어서 관리하는 데 사용되는 list, tuple, dictionary, setstring, 심지어 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 function next()에 해당 Iterator의 object를 argument로 넘겨주는 방식으로도 사용가능
    (이 방법도 내부적으로는 __next__() 를 사용함.)
    • 모든 element가 __next__()를 통해 반환되고 나면,
      이후 해당 method 호출 시 StopIteration Excpetion이 발생.
  • Iterable Object와 마찬가지로 for문에서 in 뒤에 놓여져서 iteration이 가능함.

for문으로 살펴본 IterableIterator의 관계는 다음과 같음.

 

다음과 같이 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 IterableIterator 를 만들 수 있음.

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]

 

 

위에서 만든 Iterablefor문을 사용하면 다음과 같음.

src = [1,2,3,4]
iter = DsIterable(src)

for i in iter:
    print(i)

https://ds31x.tistory.com/35

 

[Python] special methods

Python interpreter에 의해 간접적으로 호출되는 methods를 가르킴. 특징으로 double underscore __ 로 이름이 시작되고 끝난다. double underscore로 싸여있는 이름은 Python이 다른 syntax와 연결되어 사용되도록 미

ds31x.tistory.com


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으로 가지는 iterablegenerator를 비교해보라.


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

 

[Python] List Comprehension

List comprehension is an expression(표현식) that transforms a collection (not necessarily a list) into a list. list를 생성하는 expression 으로, 원본이 되는 collection 의 모든 item 혹은 일부 item들에 대해 같은 operation을 적용

dsaint31.tistory.com

 

반응형

+ Recent posts