- Iterable
- An object that has an
__iter__method which returns an iterator, or which defines a__getitem__method that can take indexes- lists, strings, tuples, files, etc
- So an iterable is an object that you can get an iterator from
- An object that has an
- Iterator
- an object with state that remembers where it is during iteration
- object that defines how to actually do the iteration—specifically what is the next element
- has a
__next__method that- returns the next value in the iteration
- updates the state to point at the next value
- keeps returning next item until it raises a
StopIterationerror
- an object used to iterate over an iterable
- Example
"HELLO"is an iterable, not an iteratoriter("HELLO")returns an iterator
Example
- link: https://stackoverflow.com/questions/9884132/what-are-iterator-iterable-and-iteration
So what does Python interpreter think when it sees
for x in obj:statement?
Look, a
forloop. Looks like a job for an iterator… Let’s get one. … There’s thisobjguy, so let’s ask him.“Mr.
obj, do you have your iterator?” (… callsiter(obj), which callsobj.__iter__(), which happily hands out a shiny new iterator_i.)OK, that was easy… Let’s start iterating then. (
x = _i.next()…x = _i.next()…)
- Basically, when you have a for loop like
for c in "helloworld", the string"helloworldis an iterable, and Python automatically gets an iterator from it by calling the__iter__()method- on each iteration of the loop, Python calls the
__next__()method of the iterator, which keeps track of the character in the string
- on each iteration of the loop, Python calls the
Custom for loop
""" 1st ver """
def my_for(iterable):
it = iter(iterable) # getting the iterator
# for i in range(len(iterable)):
# print(next(it))
while True:
try:
print(next(it))
except StopIteration:
print("end of iterator!")
break
my_for("hello")
# h
# e
# l
# l
# o
# end of iterator!
""" 2nd ver - with func """
# we add a function that we'd like to do
def my_for(iterable, func):
it = iter(iterable) # getting the iterator
while True:
try:
i = next(it)
except StopIteration:
break
else:
func(i)
my_for([1,2,3], print)
# 1
# 2
# 3
def square(x):
print(x*x)
my_for([1,2,3], square)
# 1
# 4
# 9Custom iterable/iterator
""" Making a custom Iterable Class - using iter"""
class Counter:
def __init__(self, low, high):
self.low = low
self.high = high
# iter(Counter) will return a iterator, which will have a 'next'
def __iter__(self):
return iter(range(self.low, self.high))
for n in Counter(50,55):
print(n)
# lkim@fedora:~/Desktop/python_practice$ python iterators.py
# 50
# 51
# 52
# 53
# 54
""" Making a custom Iterable Class - using custom class"""
class Counter:
def __init__(self, low, high):
self.low = low
self.high = high
# iter(Counter) will return a iterator, which will have a 'next'
def __iter__(self):
return CountIterator(self.low, self.high)
class CountIterator:
def __init__(self, low, high):
self.current = low
self.high = high
def __next__(self):
if self.current < self.high:
cur = self.current
self.current += 1
return cur
raise StopIteration
for n in Counter(50,55):
print(n)
# lkim@fedora:~/Desktop/python_practice$ python iterators.py
# 50
# 51
# 52
# 53
# 54
""" Making a custom Iterable/Iterator Class - Counter"""
# we can have BOTH by letting __iter__ return self
class Counter:
# both classes have the same init method, so ig we can merge them
def __init__(self, low, high):
self.current = low
self.high = high
# the iter function returns the object, which refers to the same instance in memory, which ALSO has __next__ defined
def __iter__(self):
return self
def __next__(self):
if self.current < self.high:
cur = self.current
self.current += 1
return cur
raise StopIteration
for n in Counter(50,55):
print(n)
# lkim@fedora:~/Desktop/python_practice$ python iterators.py
# 50
# 51
# 52
# 53
# 54