https://docs.python.org/3/tutorial/classes.html
https://docs.python.org/3/library/functions.html#iter
https://docs.python.org/3/library/stdtypes.html#typeiter
Three notions:
(1) an iterable is anything that you can iterate over
[iter() can build an iterator],
(2) an iterator is the thing that does the actual iterating
[__iter__() and __next__() methods should be present],
(3) generators are one of the simpler ways to create your own iterators
['lazy evaluation'; generator expressions and generator functions].
for item in iterable: # iteration
print(item)
L = list(iterable)
result = sum(iterable)
high = max(iterable)
low = min(iterable)
# Builtin function. reversed(sequence) # reversed object
# Generator expression. sequence = "word" gen = (sequence[i] for i in range(len(sequence)-1,-1,-1)) # generator object
# Generator function.
def Reverse(sequence): # a generator iterator is returned
"""A reverse iterator based on a sequence."""
idx = len(sequence)
while idx > 0:
idx = idx-1
yield sequence[idx]
# The other version based on a class.
class Reverse: # an instance of this class is an iterator
"""A reverse iterator based on a sequence."""
def __init__(self, sequence):
self.sequence = sequence
self.idx = len(sequence)
def __iter__(self): # for the iter() function
return self
def __next__(self): # for the next() function
if self.idx == 0:
raise StopIteration
self.idx -= 1
return self.sequence[self.idx]
next = __next__ # compatibility
# Py2 has X.next().
# Py3 has X.__next__().
# Py3 i Py2.6+ have a builtin function next(X).
# Usage (for both versions).
for char in Reverse("spam"):
print(char) # m, a, p, s
for item in Reverse([1, 2, 3, 4]):
print(item) # 4, 3, 2, 1
# iter(iterable) return iterator
# iter(callable, sentinel) return iterator
# an iterator based on a sequence
it1 = iter([1, 2]) # list_iterator object in Py3 (lists have __iter__)
it2 = iter("abcd") # str_iterator object in Py3 (strings have __iter__)
it3 = iter(it2) # all iterators are also iterables
assert it3 is it2 # the same id()
it4 = iter(range(5)) # range_iterator (range has __iter__)
it5 = iter({1:"a", 2:"b"}) # dict_keyiterator (dicts have __iter__)
# Once you have an iterator, the only thing you can do with it is get
# its next item. And you’ll get a stop iteration exception if you ask
# for the next item but there aren’t anymore items.
print(next(it1)) # 1
print(next(it1)) # 2
print(next(it1)) # StopIteration
class Dumb:
def __init__(self, sequence):
self.data = sequence
def __getitem__(self, n):
return self.data[n]
x = Dumb("word")
print(x[1]) # o
#print(x[10]) # IndexError: string index out of range
#len(x) # AttributeError: Dumb instance has no attribute '__len__'
for item in x: # iteration
print(item)
# manual iteration
y = iter(x) # make an iterator, iter() use the sequence protocol (__getitem__)
print(next(y)) # w
print(next(y)) # o
print(next(y)) # r
print(next(y)) # d
print(next(y)) # StopIteration, the iterator is exhausted
# Chunking list by two using an iterator. it = iter(range(6)) list(zip(it, it)) # [(0, 1), (2, 3), (4, 5)]
# https://www.programiz.com/python-programming/iterator
for element in iterable:
# do something with element
process(element)
# It is equivalent to the following code.
# create an iterator object from that iterable
iter_obj = iter(iterable)
# infinite loop
while True:
try:
# get the next item
element = next(iter_obj)
# do something with element
process(element)
except StopIteration:
# if StopIteration is raised, break from loop
break
class MyInt:
def __init__(self):
self.n = -1
def __call__(self):
self.n = self.n + 1
return self.n
x = MyInt() # x is a MyInt instance, a callable object
print(x()) # returns 0
print(x()) # returns 1
print(x()) # returns 2
# Preparing an iterator 'y'.
# If the callable returns 5 then y stops.
y = iter(MyInt(), 5) # y is a callable-iterator object
for i in y: # returns 0, 1, 2, 3, 4 (without 5)
print(i)
# 5 was used to stop the iterator y.
print(next(y)) # StopIteration is raised, y is exhausted
# Infinite iterators. import itertools iter_zeros1 = iter(int, 1) # int() gives 0 iter_zeros2 = iter((lambda: 0), 1) iter_zeros3 = itertools.cycle([0]) # itertools.cycle(sequence) iter_zeros4 = itertools.repeat(0) # itertools.repeat(x, times=5)