문제 두가지
아주 아주 큰 list
0부터 99,999,999까지 숫자의 제곱을 출력하는 코드를 살펴보겠습니다
for i in [i ** 2 for i in range(100000000)]:
print(i)
첫번째 결과인 0의 제곱이 출력될 때까지 얼마나 걸릴까요? 제 컴퓨터에서는 약 27초가 걸렸습니다. 그냥 0의 제곱을 출력할 뿐인데 왜 이렇게 오래 걸릴까요? 문제는 바로 [i ** 2 for i in range(100000000)]
부분에 있습니다. 이 코드는 List Comprehension이기 때문에 for loop를 돌기 전에 0부터 99,999,999까지 모든 수의 제곱을 먼저 계산합니다. 그리고 만들어진 list를 대상으로 for loop가 돕니다. 하지만 우리가 하고 싶은 것은 순차적으로 0, 1, 2, ...에 대해서 제곱값을 출력하고 싶을 뿐입니다. 굳이 99,999,999까지 미리 다 계산을 할 필요는 없는 것이죠. 이런 방식에는 두가지 문제가 있습니다.
- 첫번째 문제는 제곱값을 출력하기 위해서는 99,999,999의 제곱이 개선될 때까지 기다려야한다는 점입니다. 즉, 프로그램의 반응성이 느려집니다.
- 두번째 문제는 불필요하게 메모리를 많이 사용한다는 점입니다. 특정 시점, 예를 들어 i가 3인 시점에 우리는 0, 1, 2나 4부터 99,999,999까지의 제곱은 필요없습니다. 하지만 이 방법으로는 0부터 99,999,999까지 제곱을 모두 메모리에 가지고 있어야합니다.
아래와 같이 조금은 빠르게 코드를 바꿔볼 수도 있습니다.
f
or i in [i for i in range(100000000)]:
print(i ** 2)
list를 만들 때 제곱 계산을 하지는 않지만 여전히 0부터 99,999,999까지 list를 만들어야하기 때문에 근본적으로 앞에 두가지 문제를 가지고 있습니다.
무한한 목록 또는 끝이 언제인지 알 수 없는 목록
이번에는 다른 문제를 살펴보겠습니다. 마찬가지로 0부터 제곱을 출력하는 문제이지만 이번에는 99,999,999까지라는 제약없이 무한히 제곱을 구하고 싶다고 해보겠습니다. list는 시작과 끝이 있어야하기 때문에 이런 문제를 list로 표현하는 것 자체가 불가능합니다.
다른 예로 생방송으로 진행되고 있는 뉴스의 자막을 만드는 프로그램을 짠다고 생각해봅시다. 이 경우도 앵커의 말이 언제 끝날지 알 수 없기 때문에 유한한 목록인 list를 사용할 수가 없습니다.
iterate, iterator, iterable
이 두가지 문제를 풀기 위한 가장 좋은 도구는 iterator입니다. 네이버 영어 사전을 찾아보면 iterate은 무엇인가를 반복하다라는 뜻이라고 합니다. Merriam-Webster의 정의도 to say or do again or again and again
라고 비슷합니다. 이렇게 보면 iterate과 repeat이 같아 보이는데 프로그래밍에서는 어떤 차이가 있을까요?
Repeat은 우리가 흔히 생각하는 반복입니다. 즉 동작 A가 있다면, 이 동작을 여러번 수행하는 것입니다. 반면 iterate은 순차적인 방문에 가깝습니다. [a, b, c, d]
라는 목록이 있을 때, a, b, c, d를 각각 방문하는 동작은 repeat이 아니고 iterate입니다. 이 때문에 iterate이라는 표현은보통 배열이나 list를 대상으로 사용합니다.
Iterator는 특정 목록(배열, list 등)을 iterate(순차 방문)할 수 있게 해주는 것을 의미합니다. iterable이라는 개념도 있습니다. 이는 어떤 대상이 iterate할 수 있는지 없는지를 의미합니다. 말이 복잡한데 예를 들어 보겠습니다.
a = 20
l = [1, 2, 3, 4, 5]
a
는 iterable이 아닙니다. 아래와 같이 사용할 수 없기 때문입니다. 실제로 아래 코드를 실행하면TypeError: 'int' object is not iterable
이라는 에러가 발생니다.for i in a: print(i)
l
은 iterable입니다. 아래와 같이for
문을 사용해서 각 요소를 방문할 수 있기 때문입니다.for i in l: print(i)
- 이렇게
l
의 각 요소를 방문하는 행동을l
을 iterate한다고 합니다. - 그리고 이렇게 iterate을 하게 해주는 객체를 iterator라고 합니다.
이 코드에는 아무리 봐도 iterator라는 객체가 없는데, 저는 도대체 무엇을 보고 iterator라고 하는 것일까요?
다시 같은 코드를 살펴보겠습니다.
l = [1, 2, 3, 4, 5]
for i in l:
print(i)
print("Done")
1, 2, 3, 4, 5를 차례대로 출력하고 Done을 출력하는 간단한 코드입니다. 이 코드를 iterator를 사용해서 다시 써보겠습니다.
l = [1, 2, 3, 4, 5]
it = iter(l)
try:
while True:
i = next(it)
print(i)
except Stopiteration as e:
print("Done")
같은 일을 하지만 딱 봐도 복잡합니다. 실전에서 이렇게 쓸 일은 없습니다. 단지 설명을 위한 예시 코드일 뿐입니다. 이 코드에서 it
이 바로 iterator입니다. 즉, it
은 iterable 객체인 l
을 iterate할 수 있게 해주는 iterator입니다.
iterator 사용하기
앞의 코드를 보면 iterator를 어떻게 사용하는지 알 수 있습니다.
iter()
함수를 사용해서 iterator 객체를 만듭니다.iter()
는 인자로 받은 객체가 iterable이라면 이 객체를 위한 iterator를 반환합니다. 아니라면TypeError: 'int' object is not iterable
에러가 발생합니다.- iterator를 사용해서 순차적인 방문(iterate)을 하려면
next()
함수를 사용합니다.next()
는 인자로 받은 객체에게 다음 방문할 값을 달라고 요청하고, 이 값을 반환합니다. 인자가 iterator가 아니면TypeError: 'int' object is not an iterator
에러가 발생합니다.iter()
는 iterator를 만들기 위해서 인자로 iterable이 필요하고,next()
는 iterate을 하기 위해서 인자로 iterator가 필요합니다. 자세히 보시면 두 에러 메시지가 다릅니다. next()
를 호출했을 때 이미 목록의 끝에 도달해서 더이상 돌려줄 값이 없을 수 있습니다. 이 때는StopIteration
예외를 발생시킵니다.
이 설명을 기반으로 앞의 코드를 다시 살펴봅시다. for 문이 내부적으로 이 3단계를 해주고 있다는 것을 짐작할 수 있습니다. 실제로 list뿐 아니라 모든 iterable 객체를 for 문에 사용할 수 있습니다. 그리고 Python의 기본 collection들은 이미 iterable입니다. 이 때문에 우리는 특별한 수고없이 list를 for문에 사용할 수 있습니다.
Iterable, Iterator 만들어보기
Iterable에 대해서만 iterate을 할 수 있기 때문에 먼저 Iterable이 무엇인지 알아보겠습니다.
Iterable은 __iter__()
함수를 제공하는 클래스입니다. 즉, __iter__()
를 가지고 있다면 어떤 클래스이든 Iterable입니다.
Note: 이는 duck typing이라고 부르는 Python의 특징입니다. 자바와 같은 객체지향 언어에서 클래스를 Iterable로 만들기 위해서는 반드시 관련된 클래스나 인터페이스를 상속해야합니다. Type Checking 시스템이 상속 관계에 의존하기 때문입니다. 하지만 Python은 다른 접근법을 가지고 있습니다. 클래스의 상속 구조에 관계없이 그 클래스가 특정 type이 원하는 행동을 할 수 있다면 그 type이라고 간주하는 경우가 많습니다 (다른 것 따지지 않고 "꽥꽥"이라고 울 수 있다면 오리라고 간주). 클래스의 상속 구조에 관계없이 __iter__()
를 가지고 있다면 Iterable이라고 보는 것이 그 예입니다. 물론 Python도 상속 구조를 가지고 있습니다. Type에 대한 자세한 이야기는 이 글의 주제를 벗어나기 때문에 참고사항으로만 하고 이 정도에서 마치겠습니다.
__iter__()
가 하는 일은 무엇일까요? 앞의 코드에서 보았듯이 어떤 객체의 iterator를 만들기 위해서는 iter()
를 사용합니다. 이 때 iter()
는 인자로 받은 객체의 __iter__()
를 호출해서 이 값을 돌려줍니다. 아래에서 it1
과 it2
는 똑같이 l
의 iterator를 반환합니다.
l = [1, 2, 3, 4]
it1 = iter(l)
it2 = l.__iter__()
히지만 이 설명은 __iter__()
가 하는 일이 아니라 iter()
가 어떻게 작동하는지에 대한 설명입니다. __iter__()
가 하는 일을 알아보기 위해서 아래 코드를 살펴보겠습니다.
class MyClass:
def __iter__(self):
self.n = 0
return self
MyClass
는 Iterable입니다. __iter__()
를 가지고 있기 때문입니다. __iter__()
의 역할은 이 클래스를 위한 Iterator를 반환하는 것입니다. 그런데 코드를 보면 __iter__()
가 반환하는 것은 자기자신(self
)입니다. 갑자기 머리가 아파지는데요. Python 스펙에 따르면 __iter__()
는 자기 자신을 반환해야합니다.
Iterator
앞의 MyClass
는 Iterable이지만 Iterator는 아닙니다. Iterator가 되기 위한 조건은 다음과 같습니다.
__iter__()
메쏘드를 가져야합니다.__next__()
메쏘드를 가져야합니다.
사실 첫번째 조건은 항상 자동으로 달성된다고 볼 수 있습니다. 왜냐면 Iterable이 __iter__()
를 통해서 Iterator를 반환하는데, 이 Iterator는 자기 자신(self
)입니다. 즉, 반환된 Iterator는 이미 __iter__()
메쏘드를 가지고 있는 셈입니다. 이 때문에 모든 Iterator는 Iterable이기도 합니다.
그럼 이번에는 __next__()
에 대해서 알아보겠습니다. 앞에 설명을 반복해보겠습니다.
iter()
함수를 사용해서 iterator 객체를 만듭니다.iter()
는 인자로 받은 객체가 iterable이라면 이 객체를 위한 iterator를 반환합니다. 아니라면TypeError: 'int' object is not iterable
에러가 발생합니다.- iterator를 사용해서 순차적인 방문(iterate)을 하려면
next()
함수를 사용합니다.next()
는 인자로 받은 객체에게 다음 방문할 값을 달라고 요청하고, 이 값을 반환합니다. 인자가 iterator가 아니면TypeError: 'int' object is not an iterator
에러가 발생합니다.iter()
는 iterator를 만들기 위해서 인자로 iterable이 필요하고,next()
는 iterate을 하기 위해서 인자로 iterator가 필요합니다. 자세히 보시면 두 에러 메시지가 다릅니다. next()
를 호출했을 때 이미 목록의 끝의 도달해서 더이상 돌려줄 값이 없을 수 있습니다. 이 때는StopIteration
예외를 발생시킵니다.
__next__()
의 역할은 두가지 입니다. 다음에 반환할 값이 있다면 이를 반환하고, 아니라면(이미 끝에 왔다면) StopIteration
을 발생시킵니다.
MyClass
는 Iterable이지만 Iterator는 아니라고 했는데(__next__()
가 없기 때문에), MyClass
를 어엿한 Iterator로 만들어 보겠습니다.
class MyClass:
def __iter__(self):
self.n = 0
return self
def __next__(self):
self.n += 1
return self.n
my_class = MyClass()
it = iter(my_class)
print(next(it))
print(next(it))
1
2
이제 MyClass
는 __iter__()
를 가지고 있기 때문에 Iterable이고, __next__()
도 가지고 있기 때문에 Iterator이기도 합니다. MyClass
의 iterator를 사용하면 1부터 하나씩 증가하는 값을 얻을 수 있습니다.
MyClass
를 for loop에도 사용할 수 있습니다. 하지만 그 전에 MyClass
를 조금 수정해보겠습니다. 보시는 것처럼 __next__()
에 종료 조건이 없기 때문에 이대로 사용하면 무한 loop에 빠지게됩니다.
class MyClass:
def __iter__(self):
self.n = 0
return self
def __next__(self):
self.n += 1
if self.n > 10:
raise StopIteration
return self.n
my_class = MyClass()
for i in my_class:
print(i)
1
2
3
4
5
6
7
8
9
10
__init__()
을 활용하면 종료 조건을 인자로 줄 수도 있습니다.
class MyClass:
def __init__(self, until):
self.until = until
def __iter__(self):
self.n = 0
return self
def __next__(self):
self.n += 1
if self.n > self.until:
raise StopIteration
return self.n
my_class = MyClass(5)
for i in my_class:
print(i)
1
2
3
4
5
iterator는 클래스가 아닌 객체에 바인딩!
한가지 미묘하지만 중요한 점은 iter()
를 통해서 만들어지는 iterator는 클래스(Class)가 아닌 객체(Object)에 묶인다는 점입니다.
Note: 클래스는 데이터를 정의한 타입이고, 클래스를 실제 인스턴스화하면 객체입니다.
다르게 말하면 iter()
의 인자는 클래스가 아닌 객체여야합니다.
my_class = MyClass(5)
iter(my_class)
이 코드는 문제가 없지만
iter(MyClass)
이 코드는 TypeError: 'type' object is not iterable
라는 에러가 발생합니다. MyClass
는 인스턴스화되지 않은 클래스이고, my_class
는 MyClass
를 인스턴스화한 객체이기 때문입니다. iterator가 객체에 묶인다는 것은 중요한 시사점이 있습니다.
class MyClass:
def __init__(self, until):
self.until = until
def __iter__(self):
self.n = 0
return self
def __next__(self):
self.n += 1
if self.n > self.until:
raise StopIteration
return self.n
my_class_to_5 = MyClass(5)
my_class_to_10 = MyClass(10)
for i in my_class_to_5:
print("my_class_to_5: {}".format(i))
for i in my_class_to_10:
print("my_class_to_10: {}".format(i))
my_class_to_5: 1
my_class_to_5: 2
my_class_to_5: 3
my_class_to_5: 4
my_class_to_5: 5
my_class_to_10: 1
my_class_to_10: 2
my_class_to_10: 3
my_class_to_10: 4
my_class_to_10: 5
my_class_to_10: 6
my_class_to_10: 7
my_class_to_10: 8
my_class_to_10: 9
my_class_to_10: 10
첫번째 for 문과 두번째 for 문은 각기 다른 객체(my_class_to_5
와 my_class_to_10
)의 iterator를 만들기 때문에 두 iterator도 다른 객체입니다. 즉, 두 iterator는 별도의 내부 상태를 가지게 됩니다.
다른 예를 보겠습니다.
class MyClass:
def __iter__(self):
self.n = 0
return self
def __next__(self):
self.n += 1
if self.n > 10:
raise StopIteration
return self.n
my_class_1 = MyClass()
my_class_2 = MyClass()
it1 = iter(my_class_1)
it2 = iter(my_class_2)
print("From it1: {}".format(next(it1)))
print("From it1: {}".format(next(it1)))
print("From it1: {}".format(next(it1)))
print("From it2: {}".format(next(it2)))
From it1: 1
From it1: 2
From it1: 3
From it2: 1
같은 클래스(MyClass
)에 대해서 두 개의 객체(my_class_1
, my_class_2
)를 만들고, 이 각각의 객체에 대해서 두 개의 iterator(it1
, it2
)를 만들었습니다. it1
과 it2
는 다른 객체이고 각각의 내부 상태를 가집니다. 이 때문에 it1
이 이미 3까지 갔다고 하더라도, it2
는 1을 출력합니다.
Iterator를 만들 일이 있을까요?
많은 경우에는 Iterator를 직접 만들지 않아도 됩니다. 이미 list, set과 같은 기본 타입에는 Iterator가 구현되어 있기 때문입니다. 그렇다면 언제 Iterator를 만들어야할까요? 드디어 이 글의 시작에 언급했던 두 가지 문제로 돌아갈 때가 됐습니다.
아주 아주 큰 list
문제가 됐던 코드를 다시 보겠습니다.
for i in [i ** 2 for i in range(100000000)]:
print(i)
1억 개 정수의 제곱을 미리 다 계산해야하기 때문에 아주 오래 걸리고 메모리를 많이 차지하는 문제가 있었습니다. 이 문제를 Iterator를 사용해서 해결해보겠습니다.
class SquareList:
def __init__(self, n):
self.n = n
def __iter__(self):
self.current = 0
return self
def __next__(self):
if self.current == self.n:
raise StopIteration
result = self.current ** 2
self.current += 1
return result
for i in SquareList(100000000):
print(i)
코드를 실행하면 즉시 0, 1, 4 와 같이 출력을 시작합니다. 물론 메모리도 순간순간 필요한 만큼만 사용합니다. List Comprehension과 다르게 (for가 내부적으로) 1. 처음에는 iter()
로 iterator만 만들고 2. 필요할 때마다 next()
로 실제 제곱을 계산하기 때문입니다.
무한한 목록
무한한 목록은 애초에 list로 표현이 불가능합니다. 하지만 우리는 이미 이런 코드를 Iterator를 사용해서 작성해보았습니다.
class MyClass:
def __iter__(self):
self.n = 0
return self
def __next__(self):
self.n += 1
return self.n
이 클래스의 Iterator는 0, 1, 2, 3과 같이 무한히 정수를 만들어냅니다.
Generator
이렇게 장점이 많은 Iterator이지만 치명적인 단점이 있습니다. 작성해야할 코드가 많다는 점입니다. 이 문제를 해결하기 위한 기능이 Generator입니다. 좀 더 명확히는 __iter__()
와 __next__()
를 직접 구현하지 않고도 Iterator를 만들 수 있습니다.
Generator의 정식 이름은 Generator Function입니다. 즉 Generator도 함수입니다. 그렇다면 어떤 점이 일반 함수와 다를까요?
- Generator는
yield
라는 문구를 반드시 가져야합니다. 이에 대해서는 뒤에 다시 설명하겠습니다. - Generator Function을 실행하면 즉시 함수가 반환되고 반환값은 Iterator입니다. (정확히는 Iterator의 일종인 generator type입니다). 이 특성 때문에 Generator Function을 Iterator를 반환하는 함수라고도 부릅니다.
어떤 함수가 Generator인지 아닌지는 어떻게 판단할까요? 함수 내부에 yield
가 있다면 Generator로 간주합니다.
def f1():
print("From f1: This is f1")
print("From f1: Nice to meet you!")
def f2():
print("From f2: This is f2")
yield "From f2: Nice to meet you!"
f1()
f2()
From f1: This is f1
From f1: Nice to meet you!
<generator object f2 at 0x1cf3ea660>
f1()
은 yield
가 없는 일반함수이기 때문에 두 개의 print 문을 수행하고 함수가 끝납니다. 하지만 f2()
는 yield
가 들어있는 Generator이기 때문에 본체의 코드를 실행하지 않고 즉시 generator object를 리턴합니다. yield
의 위치는 중요하지 않습니다. yield
앞에 print가 있지만 실행되지 않은 것을 볼 수 있습니다.
yield
그렇다면 yield
의 역할은 무엇일까요? yield
는 next()
를 호출했을 때 반환할 값을 지정합니다. 즉, next()
를 호출할 때마다 yield
가 지정하는 값을 가져옵니다. __next__()
의 return
과 같은 역할입니다. 하지만 return
과 아주 커다란 차이점이 있습니다. return
을 만나면 함수가 끝나지만 yield
를 만나면 값을 반환하고 동작을 멈추지만 종료가 아닌 일시정지를 한다는 점입니다.
말이 복잡한데요, 실제 코드를 살펴보겠습니다.
def square_list(n):
current = 0
while current < n:
result = current ** 2
current += 1
yield result
for i in square_list(10):
print(i)
이 코드는 앞의 Iterator 버전을 Generator로 구현한 것입니다. 훨씬 코드가 간결합니다. 이 코드가 어떻게 작동하는지 살펴보겠습니다.
quare_list(10)
가 호출되면 함수 본문을 실행하지 않고 즉시 함수 실행이 멈추고, Iterator(정확히는 generator type)을 반환합니다. 이제 for 문은 loop에 사용할 iterator를 가졌습니다.- for 문이 첫번째 element를 갖기 위해서 이 iterator에
next()
를 호출합니다. square_list()
는 이전에 멈췄던 지점(함수의 맨처음)부터 코드를 다시 실행합니다. 첫번째next()
호출이기 때문에 함수의 처음부터 실행을 합니다.- 코드를 실행하다가
yield
를 만납니다. 함수는result
의 값을 반환하고 일시정지 상태로 들어갑니다. - for 문은
next()
가 돌려준 값(square_list()
의yield
가 돌려준 값)을i
에 저장하고 실행을 이어 갑니다. - 다음 loop 차례가 오고, for는 다시 iterator에
next()
를 호출합니다. square_list()
는 이전에 멈췄던 지점부터 코드를 다시 실행합니다.yield result
까지 실행했기 때문에 이 뒤부터 이어서 코드를 실행합니다. 이 코드에서는while current < n:
줄로 돌아갑니다. 만약yield result
다음에print("Hi")
가 있었다면print("Hi")
를 실행하고 while loop을 다시 시작했을 겁니다.
일시정지
처음 Generator를 접했을 때 혼란스러운 가장 큰 이유는 일시정지라는 개념입니다. 흔히 함수는 주~욱 실행되고 끝나기 때문에 일시정지와 재개라는 개념이 없습니다. 하지만 Generator는 함수임에도 불구하고 멈췄다 다시 시작했다를 반복합니다. 게다가 iterator를 반환하는데 iterator는 각자의 내부 상태를 가지기 때문에 더 혼란스러울 수 있습니다. Generator를 이해하는 좋은 방법 중 하나는 길지만 직관적인 Iterator 버전과 코드를 비교해보는 것입니다.
class SquareList:
def __init__(self, n):
self.n = n
def __iter__(self):
self.current = 0
return self
def __next__(self):
if self.current == self.n:
raise StopIteration
result = self.current ** 2
self.current += 1
return result
for i in SquareList(10):
print(i)
def square_list(n):
current = 0
while current < n:
result = current ** 2
current += 1
yield result
for i in square_list(10):
print(i)
이 두 버전을 번갈아 가면서 비교해봅시다. Iterator의 어떤 부분이 Generator Function으로 구현됐는지에 집중해서 보시길 추천드립니다.
StopIteration
위의 코드를 보면 Iterator의 중요한 요소 중 StopIterator
가 빠진 것을 알 수 있습니다. __iter__()
는 Generator 함수를 실행시키는 것으로 대체되고, __next__()
는 yield
와 함수 코드들로 대체가 됩니다. 하지만 어디에도 명시적으로 StopIteration
를 대체하는 코드는 없습니다. 답은 Generator Function의 코드가 끝날 때 StopIterator
가 발생한다입니다.
def square_list(n):
current = 0
while current < n:
result = current ** 2
current += 1
yield result
it = square_list(3)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
이 코드를 실행시키면 0, 1, 4가 출력되고 StopIteration
예외가 발생합니다. square_list()
코드의 while loop가 끝나고 함수도 종료됐기 때문입니다. 명시적으로 return을 사용해서 StopIteration
을 발생시킬 수도 있습니다.
def square_list(n):
current = 0
while True:
if current == n:
return
result = current ** 2
current += 1
yield result
it = square_list(3)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
Iterator와 Generator는 어떤 때 유용할까요?
먼저 데이터가 너무 커서 메모리에 다 올리기 힘들 때입니다. 예를 들어 쇼핑몰에서 모든 고객이 이번달에 지출한 금액의 평균을 알고 싶다고 해보겠습니다. 고객이 수십명에서 수백명 정도면 전체 고객 데이터를 메모리에 올리고 처리를 해도 됩니다. 하지만 다행히 장사가 잘 되서 고객이 수십만명에 이른다면 전체 데이터를 메모리에 올릴 수가 없습니다. 이 때 Iterator나 Generator를 사용한다면 한명한명의 고객 데이터만 메모리에 올려서 문제없이 처리할 수 있습니다.
또다른 경우는 데이터의 끝을 모르는 경우입니다. 주로 데이터가 동적으로 만들어지는 경우가 그렇습니다. 예를 들어 트위터에 특정 해쉬태그가 붙은 피드가 올라올 때마다 이 피드를 따로 저장하고 싶다고 해보겠습니다. 트위터 피드는 계속해서 생성되는 데이터이기 때문에 무한한 데이터이고 한번에 다 메모리로 읽는 것이 불가능합니다. 이 때 Iterator나 Generator를 사용하면 현재 가능한 피드를 순차적으로 처리할 수 있습니다.
하지만 Iterator와 Generator에는 아주 중요한 제약이 있습니다. 바로 데이터에 순차적으로 접근한다는 점입니다. 순차적인 접근에 반대 개념은 임의 접근(Random Access)입니다. 배열의 a[i]
와 같은 방식이 임의 접근입니다. 지금까지 본것처럼 Iterator는 데이터를 얻기 위해서 next()
만 제공합니다. 즉, 바로 다음 데이터만 알 수 있는 것입니다.
'Python' 카테고리의 다른 글
Python Callable (0) | 2022.07.11 |
---|---|
Python 메소드의 첫번째 인자인 `self`는 무엇인가? (0) | 2022.03.24 |
List Comprehension (0) | 2021.05.04 |
Jupyter Lab (0) | 2021.05.01 |
*args, **kwargs (2) | 2020.06.23 |