best way to convert generator into iterator class

  • Last Update :
  • Techknowledgy :

Using yield in a function turns it into a generator function, which means that it returns a generator when it's called. But even without using yield, nothing's preventing you from creating a generator and returning it, like so:

def common_divisors_generator(n, m):
   factors_n = [i
      for i in range(1, n + 1) if n % i == 0
   ]
factors_m = [i
   for i in range(1, m + 1) if m % i == 0
]

def gen():
   for fn in factors_n:
   for fm in factors_m:
   if fn == fm:
   yield fn

return gen()

And if you're using a class, there's no need to implement a __next__ method. You can just use yield in the __iter__ method:

class CommonDivisorsIterator(object):
   def __init__(self, n, m):
   self.factors_n = [i
      for i in range(1, n + 1) if n % i == 0
   ]
self.factors_m = [i
   for i in range(1, m + 1) if m % i == 0
]

def __iter__(self):
   for fn in self.factors_n:
   for fm in self.factors_m:
   if fn == fm:
   yield fn

Suggestion : 2

The itertools module has a few functions that can be used to address this task. The first is the itertools.dropwhile() function. To use it, you supply a function and an iterable. The returned iterator discards the first items in the sequence as long as the supplied function returns True. Afterward, the entirety of the sequence is produced.,To use such a function, you iterate over it using a for loop or use it with some other function that consumes an iterable (e.g., sum(), list(), etc.). For example:,The dropwhile() and islice() functions are mainly convenience functions that you can use to avoid writing rather messy code such as this:,This example is based on skipping the first items according to a test function. If you happen to know the exact number of items you want to skip, then you can use itertools.islice() instead. For example:

To manually consume an iterable, use the next() function and write your code to catch the StopIteration exception. For example, this example manually reads lines from a file:

with open('/etc/passwd') as f:
   try:
   while True:
   line = next(f)
print(line, end = '')
except StopIteration:
   pass

Normally, StopIteration is used to signal the end of iteration. However, if you’re using next() manually (as shown), you can also instruct it to return a terminating value, such as None, instead. For example:

with open('/etc/passwd') as f:
   while True:
   line = next(f, None)
if line is None:
   break
print(line, end = '')

The following interactive example illustrates the basic mechanics of what happens during iteration:

>>> items = [1, 2, 3]
>>> # Get the iterator
>>> it = iter(items) # Invokes items.__iter__()
>>> # Run the iterator
>>> next(it) # Invokes it.__next__()
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
      StopIteration
      >>>

Typically, all you need to do is define an __iter__() method that delegates iteration to the internally held container. For example:

class Node:
   def __init__(self, value):
   self._value = value
self._children = []

def __repr__(self):
   return 'Node({!r})'.format(self._value)

def add_child(self, node):
   self._children.append(node)

def __iter__(self):
   return iter(self._children)

# Example
if __name__ == '__main__':
   root = Node(0)
child1 = Node(1)
child2 = Node(2)
root.add_child(child1)
root.add_child(child2)
for ch in root:
   print(ch)
# Outputs Node(1), Node(2)

If you want to implement a new kind of iteration pattern, define it using a generator function. Here’s a generator that produces a range of floating-point numbers:

def frange(start, stop, increment):
   x = start
while x < stop:
   yield x
x += increment

To use such a function, you iterate over it using a for loop or use it with some other function that consumes an iterable (e.g., sum(), list(), etc.). For example:

>>>
for n in frange(0, 4, 0.5):
   ...print(n)
   ...
   0
0.5
1.0
1.5
2.0
2.5
3.0
3.5
   >>>
   list(frange(0, 1, 0.125))[0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875] >>>

The mere presence of the yield statement in a function turns it into a generator. Unlike a normal function, a generator only runs in response to iteration. Here’s an experiment you can try to see the underlying mechanics of how such a function works:

>>> def countdown(n):
... print('Starting to count from', n)
... while n > 0:
... yield n
... n -= 1
... print('Done!')
...

>>> # Create the generator, notice no output appears
>>> c = countdown(3)
>>> c
<generator object countdown at 0x1006a0af0>

   >>> # Run to first yield and emit a value
   >>> next(c)
   Starting to count from 3
   3

   >>> # Run to the next yield
   >>> next(c)
   2

   >>> # Run to next yield
   >>> next(c)
   1

   >>> # Run to next yield (iteration stops)
   >>> next(c)
   Done!
   Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
         StopIteration
         >>>

By far, the easiest way to implement iteration on an object is to use a generator function. In Recipe 4.2, a Node class was presented for representing tree structures. Perhaps you want to implement an iterator that traverses nodes in a depth-first pattern. Here is how you could do it:

class Node:
   def __init__(self, value):
   self._value = value
self._children = []

def __repr__(self):
   return 'Node({!r})'.format(self._value)

def add_child(self, node):
   self._children.append(node)

def __iter__(self):
   return iter(self._children)

def depth_first(self):
   yield self
for c in self:
   yield from c.depth_first()

# Example
if __name__ == '__main__':
   root = Node(0)
child1 = Node(1)
child2 = Node(2)
root.add_child(child1)
root.add_child(child2)
child1.add_child(Node(3))
child1.add_child(Node(4))
child2.add_child(Node(5))

for ch in root.depth_first():
   print(ch)
# Outputs Node(0), Node(1), Node(3), Node(4), Node(2), Node(5)

Suggestion : 3

Each time we call the next method on the iterator gives us the next element. If there are no more elements, it raises a StopIteration.,The built-in function iter takes an iterable object and returns an iterator.,Problem 9: The built-in function enumerate takes an iteratable and returns an iterator over pairs (index, value) for each value in the source.,The __iter__ method is what makes an object iterable. Behind the scenes, the iter function calls __iter__ method on the given object.

>>>
for i in [1, 2, 3, 4]:
   ...print(i)
   ...
   1
2
3
4
>>>
for c in "python":
   ...print(c)
   ...
   p
y
t
h
o
n
>>>
for k in {
      "x": 1,
      "y": 2
   }:
   ...print(k)
   ...
   y
x
>>>
for line in open("a.txt"):
   ...print(line, end = "")
   ...
   first line
second line
>>> ",".join(["a", "b", "c"])
'a,b,c' >>>
",".join({
   "x": 1,
   "y": 2
})
'y,x' >>>
list("python")['p', 'y', 't', 'h', 'o', 'n'] >>>
   list({
      "x": 1,
      "y": 2
   })['y', 'x']
>>> x = iter([1, 2, 3])
>>> x
<listiterator object at 0x1004ca850>
   >>> next(x)
   1
   >>> next(x)
   2
   >>> next(x)
   3
   >>> next(x)
   Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
         StopIteration

Suggestion : 4

By Bernd Klein. Last modified: 24 Apr 2022.

cities = ["Paris", "Berlin", "Hamburg",
   "Frankfurt", "London", "Vienna",
   "Amsterdam", "Den Haag"
]
for location in cities:
   print("location: " + location)
location: Paris
location: Berlin
location: Hamburg
location: Frankfurt
location: London
location: Vienna
location: Amsterdam
location: Den Haag
expertises = ["Python Beginner",
   "Python Intermediate",
   "Python Proficient",
   "Python Advanced"
]
expertises_iterator = iter(expertises)
print("Calling 'next' for the first time: ", next(expertises_iterator))
print("Calling 'next' for the second time: ", next(expertises_iterator))
Calling 'next'
for the first time: Python Beginner
Calling 'next'
for the second time: Python Intermediate
other_cities = ["Strasbourg", "Freiburg", "Stuttgart",
   "Vienna / Wien", "Hannover", "Berlin",
   "Zurich"
]

city_iterator = iter(other_cities)
while city_iterator:
   try:
   city = next(city_iterator)
print(city)
except StopIteration:
   break
Strasbourg
Freiburg
Stuttgart
Vienna / Wien
Hannover
Berlin
Zurich

Suggestion : 5

Last Updated : 31 Aug, 2021,GATE CS 2021 Syllabus

Syntax : 

iter(object)
iter(callable, sentinel)

Exception :  

If we call the iterator after all the elements have
been iterated, then StopIterationError is raised.

Output : 

a
e
i
o
u

Print the range without iter()
Eating more Pizzas, counting 2
Eating more Pizzas, counting 3
Eating more Pizzas, counting 4
Eating more Pizzas, counting 5

Print the range using iter()

Eating more Pizzas, counting 2
Eating more Pizzas, counting 3
Eating more Pizzas, counting 4
Eating more Pizzas, counting 5

Dead on overfood, GAME OVER

Print the range without iter()
Eating more Pizzas, counting 2
Eating more Pizzas, counting 3
Eating more Pizzas, counting 4
Eating more Pizzas, counting 5

Print the range using iter()

Eating more Pizzas, counting 2
Eating more Pizzas, counting 3
Eating more Pizzas, counting 4
Eating more Pizzas, counting 5

Dead on overfood, GAME OVER

Suggestion : 6

The easiest ways to make our own iterators in Python is to create a generator.,And when you’re considering how to create your own iterator, think of generator functions and generator expressions.,Let’s make our own iterators. We’ll start be re-inventing the itertools.count iterator object.,To make an iterator you could create an iterator class, a generator function, or a generator expression. Which way is the best way though?

1
2
3
>>> favorite_numbers = [6, 57, 4, 7, 68, 95]
>>> iter(favorite_numbers)
<list_iterator object at 0x7fe8e5623160>
1
2
3
4
5
6
>>> favorite_numbers = [6, 57, 4, 7, 68, 95] >>>
   my_iterator = iter(favorite_numbers) >>>
   next(my_iterator)
6
   >>>
   next(my_iterator)
57
1
2
>>> from itertools
import repeat
   >>>
   lots_of_fours = repeat(4, times = 100_000_000)