Generator
Iterable Objects
Iterable is an object, which one can iterate over
from collections.abc import Iterator, Iterable
# List
l = [1, 2, 3, 4, 5, 6]
isinstance(l, Iterable) # True
# Range
r = range(10)
isinstance(r, Iterable) # True
# Tuple
t = (1, 2, 3, 4)
isinstance(t, Iterable) # True
# String
s = 'Hello World!'
isinstance(s, Iterable) # True
# Dict
d = {'x':1, 'y':2}
isinstance(d, Iterable) # True
Iterator
Iterator is an object, which is used to iterate over an iterable object using __next__() method
# List
l = [1, 2, 3, 4, 5, 6]
obj = iter(l) # list_iterator
isinstance(obj, Iterator) # True
# Range
r = range(10)
obj = iter(r) # range_iterator
isinstance(obj, Iterator) # True
# Tuple
t = (1, 2, 3, 4)
obj = iter(t) # tuple_iterator
isinstance(obj, Iterator) # True
# String
s = 'Hello World!'
obj = iter(s) # str_iterator
isinstance(obj, Iterator) # True
# Dict
d = {'x':1, 'y':2}
obj = iter(d) # dict_keyiterator
isinstance(obj, Iterator) # True
l = [1, 2, 3, 4, 5, 6]
obj = iter(l) # iterator
try:
while True:
#if no more element, next raise StopIteration
item = next(obj) # int
except StopIteration:
pass
Implement Iterable and Iterator as Classes
Iterable and iterator are the same object
class yrange:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self): # make an object to be iterable
print('iter')
self.i = 0
return self # return an iterator
def __next__(self): # make an object to be iterator
print('next')
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
y = yrange(10)
isinstance(y, Iterable) # True
isinstance(y, Iterator) # True
# use as an iterable
y = yrange(3)
# call _iter_ to initialize, then call __next__ to iterate
for i in y: # print 0, 1, 2
print(i)
for i in y: # print 0, 1, 2
print(i)
# use as an iterator
y = yrange(3)
try:
# call _next__ to iterate
while True: # print 0, 1, 2
temp = next(y) # int
print(temp)
except StopIteration:
pass
try:
while True: # print nothing, iterator has been comsumed
temp = next(y) # int
print(temp)
except StopIteration:
pass
Iterable and iterator are not the same object
# define iterable
class zrange:
def __init__(self, n):
self.n = n
def __iter__(self):
print('iter')
return zrange_iter(self.n)
# define iterator
class zrange_iter:
def __init__(self, n):
self.i = 0
self.n = n
def __next__(self):
print('next')
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
z = zrange(3)
isinstance(z, Iterable) # True
isinstance(z, Iterator) # False
# used as an iterable
z = zrange(3)
for i in z: # call _iter_ to initialize, then call __next__ to iterate
print(i)
Generator
Generator is iterator, can be consumed in a single iteration
import types
# list
l = [x*x for x in range(4)]; # [0, 1, 4, 9]
print(l)
# generator
g = (x*x for x in range(4));
isinstance(g, Iterable) # True
isinstance(g, Iterator) # True
isinstance(g, types.GeneratorType) # True
for i in g: # 0 1 4 9
print(i)
for i in g: # do nothing, g has been consumed in the above for loop
print(i)
Implement Generator
def grange(n):
i = 0;
while i < n:
yield i;
i += 1
g = grange(10);
isinstance(g, Iterable) # True
isinstance(g, Iterator) # True
isinstance(g, types.GeneratorType) # True
# use as an iterable
for i in g:
print(i)
for i in g: # do not print anything, g has been consumed
print(i)
# use as an iterator
try:
while True:
temp = next(g) # int
print(temp)
except StopIteration:
pass
try:
while True: # print nothing, iterator has been comsumed
temp = next(g) # int
print(temp)
except StopIteration:
pass
Memory Size
import sys
# list, iterable
l = [x*x for x in range(100)]
sys. getsizeof(l) # 920
# list iterator
obj = iter(l)
sys. getsizeof(obj) # 48
# generator, iterable, iterator
g = (x*x for x in range(100))
sys. getsizeof(g) # 112
obj = iter(g)
sys. getsizeof(obj) # 112
# range, iterable
r = range(100)
sys. getsizeof(r) # 48
obj = iter(r)
sys. getsizeof(obj) # 48
Reference