youngfromnowhere
[Python] Generator and yield 본문
참고자료
Data Structure & Algorithms in Python, Michael T. Goodrich (이하 DSnA)
Python 공식문서(https://docs.python.org/3.12/)
Iteratable을 구현하려면, iterator를 반환하는 __iter__() method를 구현해야한다.
https://youngnowhere.tistory.com/58
[Python] Iterator
참고자료Data Structure & Algorithms in Python, Michael T. Goodrich (이하 DSnA)Python 공식문서(https://docs.python.org/3.12/) Python 공식문서에서는 Iterator를 다음과 같이 정의하고 있다.An object representing a stream of data. R
youngnowhere.tistory.com
이전글 에서는 MyIterable.__iter__() method가 MyIterator 객체를 반환하도록 하여서 MyIterable이 iterable의 조건을 충족하도록 하였다.
그런데 이미 구현되어있는 iterator class가 없는 상황에서 iterable을 구현하고자 할 때는 어떻게 해야 할까?
Generator
DSnA 에서는 generator를 다음과 같이 소개한다.
the most convenient thechnique for creating iterators in Python is through the use of generators.
Python documentation에서는 다음과 같이 정의되어 있다.
generator
A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.Usually refers to a generator function, but may refer to a generator iterator in some contexts. In cases where the intended meaning isn’t clear, using the full terms avoids ambiguity.
- 'generator iterator'를 반환하는 function
- 'yield' expression 을 포함한다.
- yield expression : for-loop에서 쓰이거나 next() function으로 하나씩 가져올 수 있는 series of values를 만들어낸다.
- 보통 generator라고 하면 (지금 설명하고 있는)generator function을 의미한다. 가끔 generator function이 반환하는 generator iterator를 의미하기도 한다.
그렇다면 generator가 반환(return)하는 generator iterator란 무엇일까?
generator iterator
An object created by a generator function.Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator iterator resumes, it picks up where it left off (in contrast to functions which start fresh on every invocation).
- generator function이 반환하는 object
- 각각의 yield는 실행 상태(local variables와 try-statements들을 포함)를 기억한 상태로 임시적으로 processing을 멈춘다.
- generator iterator가 다시 시작될 때는, 정지했던 곳에서 다시 시작한다. (일반적인 functoin이 매 invocation마다 처음부터 다시 시작하는 것과 대조된다.)
요약하면, return 대신 yield를 써서 구현한 function이 바로 generator이며
이 generator를 호출할때 반환되는 것이 generator iterator라는 것이다.
이 generator iterator는 iterator와 마찬가지로 for-loop에서 쓰이거나 next()를 통해 순차적으로 item을 꺼내올 수 있다.
예문으로 확인해보자.
#!/usr/bin/env python3
def factors_gen(n):
for k in range(1,n+1):
if n%k == 0:
yield k
if __name__ == '__main__':
gi = factors_gen(10) #returns generator-iterator
print(gi)
print(next(gi))
print(next(gi))
print(next(gi))
print(next(gi))
gi_2 = factors_gen(12)
for i in gi_2:
print(i)
gi_3 = factors_gen(8)
gi_iter = iter(gi_3)
print(gi_3)
print(gi_iter)
파일 실행 결과는 다음과 같다.
<generator object factors_gen at 0x7473e8611540>
1
2
5
10
1
2
3
4
6
12
<generator object factors_gen at 0x7473e8611460>
<generator object factors_gen at 0x7473e8611460>
- factors_gen의 호출하여 반환되는 generator-iterator를 gi, gi_2에 할당
- __next__()를 호출할 때마다 factors_gen 내부의 코드가 실행되는데, yield를 만날때마다 값 하나를 반환하고 멈춘다.
- 다음 next(gi) 호출 때 다시 내부의 코드가 실행되는데, 정지되었던 시점에서 다시 loop가 돈다.
- __iter__()를 호출할 떄 자기 자신을 반환한다.
즉, generator-iterator는 iterator의 정의를 모두 만족한다.
Iterable 구현하기
사용자 정의 Iterable을 구현하려면, __iter__() method를 generator function으로 구현하면 된다.
obj = SomeClass() 일 때, SomeClass.__iter__() method가 generator functon으로 구현되어 있으면,
iter(obj)를 호출할 때
SomeClass.__iter__(obj)는 generator-iterator를 반환한다.
위에서 살펴본 바와 같이 generator-iterator는 iterator의 모든 정의를 만족한다(즉 그냥 iterator다)
SomeClass의 __iter__()가 Iterator를 반환하므로 SomeClass는 iterable이 된다.
x = iter(obj) 와 같이 x 에 할당하면
next(x)를 통해 data를 순서대로 꺼내오거나 for-loop에 x를 쓸 수 있다.
#!/usr/bin/env python3
class Node:
def __init__(self, element, next):
self._element = element
self._next = next
class GenIterable:
def __init__(self, first, second, third):
self._first = Node(first, None)
self._second = Node(second, None)
self._third = Node(third, None)
self._first._next = self._second
self._second._next = self._third
self.cursor = self._first
def __iter__(self):
while self.cursor is not None:
res = self.cursor._element
yield res
self.cursor = self.cursor._next
if __name__ == '__main__':
gen_iterable = GenIterable('first element','second element','third element')
gen_iterator = iter(gen_iterable)
while True:
try:
print(next(gen_iterator))
except StopIteration:
print("End of gen_iterator")
break
another_iterable = GenIterable('young','from','nowhere')
for i in another_iterable:
print(i)
파일의 실행결과는 다음과 같다.
first element
second element
third element
End of gen_iterator
young
from
nowhere
'Python' 카테고리의 다른 글
[Python] recursive generator (0) | 2024.12.03 |
---|---|
[Python] Iterator (0) | 2024.10.23 |
[Python] pyenv로 가상환경 구축하기. (0) | 2023.09.19 |
[Python] python의 special methods (0) | 2022.11.17 |
[Python] Decorator3. Property (0) | 2022.11.16 |