#Iterators and Iterable
#Iterable means something that can be looped over or specifically object needs to return iterable object from its dunder iter method and the iterator thats returning must find dunder next method which access elements in container one at a time.
#Something thats Iteratable doesnot mean it's a iterator
#Iterator means it's an object with a state, so it remembers where's at during it's iteration, knows how to fetch next value using __next method and when doesn't have next value it raises StopIteration exception.
nums = [1,2,3]
for num in nums:
print(num)
#something can be iteratable if it has __iter__ method.
print(dir(nums))
#we can see list has this method
print(next(nums))
#it actually tries to run dunder next method on that object and list doesnot have dunder next method
#when we ran dunder iter method, it returns iterators for us
i_nums = nums.__iter__() # or iter(nums)
print(i_nums)
print(dir(i_nums))
print(next(i_nums))
print(next(i_nums))
#iterators can only go forward and can't be reset
nums = [1,2,3]
i_nums = iter(nums)
while True:
try:
print(next(i_nums))
except StopIteration:
break
class MYRange():
def __init__(self,start,end):
self.start = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.start >= self.end:
raise StopIteration
current = self.start
self.start += 1
return current
nums = MYRange(1,10)
for num in nums:
print(num)
print(next(nums))
print(next(nums))
print(next(nums))
print(next(nums))
print(next(nums))
#same problem using generator
def gen_func(start,end):
current = start
while current < end:
yield current
current += 1
my_var = gen_func(1,10)
for v in my_var:
print(v)
print(next(my_var))
print(next(my_var))
print(next(my_var))
print(next(my_var))
print(next(my_var))
#splitting words from sentence
class Sentence():
def __init__(self,sent):
self.sent = sent
self.index = 0
self.words = self.sent.split()
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.words):
raise StopIteration
index = self.index
self.index += 1
return self.words[index]
words = Sentence("This is a test")
for word in words:
print(word)
#using next method
print(next(words))
print(next(words))
print(next(words))
print(next(words))
#same problem using generator
def my_gen(sent):
for word in sent.split():
yield word
words = my_gen("this is a test")
for word in words:
print(word)
print(next(words))
print(next(words))
print(next(words))