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
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)
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
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
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
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)