-----------------------------------iterator---------------------------------
for i in range(5):
print i,
가 내부적으로 하는 일은 다음과 비슷.
tmplist = [0,1,2,3,4]
if not '__iter__' in dir(tmplist):
raise TypeError
tmpiter = iter(tmplist)
#또는, tmpiter = tmplist.__iter__()
whlie(True):
print tmpiter.next()
즉, 대상이 되는 iterable한 객체를 받아, next()가 없어 StopIteration 예외가 처리되며 끝나게 된다.
-----------------------------------generator---------------------------------
generator object라 만드는 대상이란 바로 데이터 그 자체다..(python.org의 정의에 따르면 "Generators are a simple and powerful tool for creating iterators."라고 설명된다. 그런데 여기서 말하는 iterator는 iterable object에 가까울 뿐, iter([1,2,3])했을 때 나오는 listiterator와 동일한 의미로서의 iterator는 아니다.)
다시말해, iterator에는 두가지 종류가 있어, 하나는 메모리에 모두 올라와 있는 데이터에 접근을 하는 가장 일반적인 형태이고, 다른 하나(generator와 연관)는 데이터를 생성하면서 순회접근 하는 형태이다. 자세한 내용은 아래에서 다시 설명한다.
generator란? generator object(즉 iterator)를 generate(생성)하는 함수
generator object란? 데이터를 generate(생성)하는 객체(object)
>>>def generatorYielder():
... X=[3,6,9]
... Y=[2,4,7]
... for x,y in zip(X,Y):
... yield x*y
이 함수가 제너레이터가 된다.
>>> generatorObj1 = generatorYielder()
>>> generatorObj2 = generatorYielder()
>>> generatorObj3 = generatorYielder()
>>> for i in generatorObj1:
... print i,
6 24 63
>>> for i in generatorObj1:
... print i,
아무것도 나오지 않음.
>>> for i in generatorObj2:
... print i,
6 24 63
>>> for i in generatorObj3:
... print i,
6 24 63
주의:
generatorObj1과 generatorObj2는 서로 다른 독립된 객체다. generatorObj1이 stopIteration 예외를 발생시킨다해도 generatorObj2와는 전혀 무관하다. 이를 증명하는 한 예는, 바로 다음과 같을 것이다.
>>> generatorYielder().next()
3
>>> generatorYielder().next()
3
>>> generatorYielder().next()
3
고찰:
그렇다면 정말, generator로 생성된 객체로서의 iterator와 iter(iterable object)로 생성된 iterator가 완벽히 같은 종류의 객체들인가?
>>> type(generator2())
<type 'generator'>
>>> type(iter([3,12,27]))
<type 'listiterator'>
즉, 일반적인 iterator는 이미 데이터가 결정되어 메모리에 저장되어 있는 객체 속 element들에 대한 단순한 방문이라면, generator는 데이터를 그 순간순간 생성 (또는 접근) 한다는 점이 다르다.
정리:
iterator는 이미 메모리에 전부 올라와 있거나, 모두 메로리로 올려도 부담이 없는 작은 규모의 데이터에 합당하다. 하지만 큰 데이터를 다루는 경우에는, 성능 및 자원 관리의 측면에 있어서라도 부분적인 접근에 따라 생성 또는 접근하는 것이 효율적이라 generator를 이용하게 된다.
이 글의 제목인 iterator vs generator에 걸맞는 두 내장함수는 range vs xrange이다.
range(10)은 0부터 9까지를 요소로 하는 리스트 객체를 리턴하는 반면, xrange(10)는 generator에 가깝다.
============================================================
generator mock 구현.
class genMock:
"""simple implementation for generator mock"""
def __init__(self,n,fn):
self.idx=0
self.end=n
self.fn=fn
def __iter__(self):
return self
def next(self):
if self.idx==self.end:
raise StopIteration
tmp = self.idx
self.idx+=1
return self.fn(tmp)
if __name__ == "__main__":
for i in genMock(10, lambda x:x*x*x):
print i,
#result
#0 1 8 27 64 125 216 343 512 729
<약간의 해설>
작동 원리는 다음과 같다.
obj = genMock(10, lambda x:x*2)
iterObj = iter(obj)
while(True):
print iterObj.next(),
그런데 위는 다음과 전적으로 동일하다.
obj = genMock(10, lambda x:x*2)
while(True):
print obj.next(),
for i in range(5):
print i,
가 내부적으로 하는 일은 다음과 비슷.
tmplist = [0,1,2,3,4]
if not '__iter__' in dir(tmplist):
raise TypeError
tmpiter = iter(tmplist)
#또는, tmpiter = tmplist.__iter__()
whlie(True):
print tmpiter.next()
즉, 대상이 되는 iterable한 객체를 받아, next()가 없어 StopIteration 예외가 처리되며 끝나게 된다.
-----------------------------------generator---------------------------------
generator object라 만드는 대상이란 바로 데이터 그 자체다..(python.org의 정의에 따르면 "Generators are a simple and powerful tool for creating iterators."라고 설명된다. 그런데 여기서 말하는 iterator는 iterable object에 가까울 뿐, iter([1,2,3])했을 때 나오는 listiterator와 동일한 의미로서의 iterator는 아니다.)
다시말해, iterator에는 두가지 종류가 있어, 하나는 메모리에 모두 올라와 있는 데이터에 접근을 하는 가장 일반적인 형태이고, 다른 하나(generator와 연관)는 데이터를 생성하면서 순회접근 하는 형태이다. 자세한 내용은 아래에서 다시 설명한다.
generator란? generator object(즉 iterator)를 generate(생성)하는 함수
generator object란? 데이터를 generate(생성)하는 객체(object)
예1) Generator Expression
>>> X=[3,6,9]
>>> Y=[2,4,7]
>>> ge=(x*y for x,y in zip(X,Y))
>>> for i in ge:
... print i,
6, 24, 63
>>> type(ge)
<type 'generator'>
예2) Generator (regular functions but use the yield statement)>>> X=[3,6,9]
>>> Y=[2,4,7]
>>> ge=(x*y for x,y in zip(X,Y))
>>> for i in ge:
... print i,
6, 24, 63
>>> type(ge)
<type 'generator'>
>>>def generatorYielder():
... X=[3,6,9]
... Y=[2,4,7]
... for x,y in zip(X,Y):
... yield x*y
이 함수가 제너레이터가 된다.
>>> generatorObj1 = generatorYielder()
>>> generatorObj2 = generatorYielder()
>>> generatorObj3 = generatorYielder()
>>> for i in generatorObj1:
... print i,
6 24 63
>>> for i in generatorObj1:
... print i,
아무것도 나오지 않음.
>>> for i in generatorObj2:
... print i,
6 24 63
... print i,
6 24 63
generatorObj1과 generatorObj2는 서로 다른 독립된 객체다. generatorObj1이 stopIteration 예외를 발생시킨다해도 generatorObj2와는 전혀 무관하다. 이를 증명하는 한 예는, 바로 다음과 같을 것이다.
>>> generatorYielder().next()
3
>>> generatorYielder().next()
3
>>> generatorYielder().next()
3
즉, 항상 새로운 iterator 객체(즉, generatorObj)를 만들기때문에 첫번째 데이터만 리턴하게 되는 꼴이다.
고찰:
그렇다면 정말, generator로 생성된 객체로서의 iterator와 iter(iterable object)로 생성된 iterator가 완벽히 같은 종류의 객체들인가?
>>> type(generator2())
<type 'generator'>
>>> type(iter([3,12,27]))
<type 'listiterator'>
이 둘은 iterator라는 큰 개념으로 불림에는 차이가 없다. 즉, iterator의 두 종류가 각각 실현되었을 뿐이다. 즉 둘은 형제이나 같지는 않다. 자세한 설명은 아래 사이트에서 발췌한다.
Everything you can use "for... in..." on is an iterable: lists, strings, files... These iterables are handy because you can read them as much as you wish, but you store all the values in memory and it's not always what you want when you have a lot of values.
Generators are iterators, but you can only iterate over them once. It's because they do not store all the values in memory, they generate the values on the fly:
정리:
iterator는 이미 메모리에 전부 올라와 있거나, 모두 메로리로 올려도 부담이 없는 작은 규모의 데이터에 합당하다. 하지만 큰 데이터를 다루는 경우에는, 성능 및 자원 관리의 측면에 있어서라도 부분적인 접근에 따라 생성 또는 접근하는 것이 효율적이라 generator를 이용하게 된다.
이 글의 제목인 iterator vs generator에 걸맞는 두 내장함수는 range vs xrange이다.
range(10)은 0부터 9까지를 요소로 하는 리스트 객체를 리턴하는 반면, xrange(10)는 generator에 가깝다.
============================================================
generator mock 구현.
class genMock:
"""simple implementation for generator mock"""
def __init__(self,n,fn):
self.idx=0
self.end=n
self.fn=fn
def __iter__(self):
return self
def next(self):
if self.idx==self.end:
raise StopIteration
tmp = self.idx
self.idx+=1
return self.fn(tmp)
if __name__ == "__main__":
for i in genMock(10, lambda x:x*x*x):
print i,
#result
#0 1 8 27 64 125 216 343 512 729
<약간의 해설>
작동 원리는 다음과 같다.
obj = genMock(10, lambda x:x*2)
iterObj = iter(obj)
while(True):
print iterObj.next(),
그런데 위는 다음과 전적으로 동일하다.
obj = genMock(10, lambda x:x*2)
while(True):
print obj.next(),
왜냐하면, genMock의 __iter__ 메소드가 그냥 객체 자신을 리턴하기 때문이다.
이는 일반 iterator와 좀 많이 다른 형태이다.
for i in range(10):
print i,
이는 다음과 같이 변환된다.
list1 = [0,1,2,3,4,5,6,7,8,9]
iterList1 = iter(list1)
while(True):
print iterList1.next(),
즉, list1은 next라는 메소드가 없지만, iter 내장함수의 결과로 나온 listiterator 객체는 next메소드를 갖는 인스턴스다. 즉, for문의 in 뒤에 오는 객체는 단지 next메소드만 불릴 수 있으면 무엇이나 올 수 있다는 것을 이용해 genMock을 이용했기에, next()메소드 정의가 반드시 필요하며, __iter__ 메소드도 자기자신(self)를 리턴하도록 조작하는 것이 불가피하다.
마지막으로, generator를 리턴하는 클래스 예이다.
class test():
def __init__(self, li):
self.li = li
def __iter__(self):
for i in self.li:
yield i
마지막으로, generator를 리턴하는 클래스 예이다.
class test():
def __init__(self, li):
self.li = li
def __iter__(self):
for i in self.li:
yield i
>>> a = test(10)
>>> for i in a: print i,
...
0 1 2 3 4 5 6 7 8 9
>>> for i in a: print i,
...
0 1 2 3 4 5 6 7 8 9
>>> for i in a: print i,
...
0 1 2 3 4 5 6 7 8 9
>>>
이쯤되면, 이 경우엔 왜 여러번 불러도 stopIteration 예외가 발생하지 않는지 이해하리라 믿는다.
댓글
댓글 쓰기