If interrupted, iteration over an iterable does not resume at the point of interruption.
If iteration is interrupted and resumed, the next iteration returns to the beginning.
>>> for p in 'abcd' :
... print(p)
... if p == 'b' : break
...
a
b
>>> for p in 'abcd' :
... print(p)
...
a
b
c
d
>>>
Iterators within Python allow for interruption of the iteration and resumption at the point
immediately after that at which interruption occurred.
To create an iterator, use built-in function iter() with an iterable as argument.
>>> L1 = [1,2,3,4]
>>> it1 = iter(L1)
>>> it1
<list_iterator object at 0x101a95a90>
>>> v0 = next(it1) ; v0
1
>>> v1 = next(it1) ; v1
2
>>> v2 = next(it1) ; v2
3
>>> v3 = next(it1) ; v3
4
>>> v4 = next(it1) ; v4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
The built-in function next(....) accepts a default argument. If provided, it will be returned if the iterator is exhausted.
>>> L1 = [1,2,3]
>>> it1 = iter(L1)
>>> v0 = next(it1) ; v0
1
>>> v1 = next(it1) ; v1
2
>>> v2 = next(it1, None) ; v2
3
>>> v3 = next(it1, None) ; v3
>>>
An iterator can behave like an iterable:
>>> L1 = [1,2,3]
>>> it1 = iter(L1)
>>> for p in it1 : print(p)
...
1
2
3
>>>
>>> L1 = list(range(8)) ; L1
[0, 1, 2, 3, 4, 5, 6, 7]
>>> it1 = iter(L1)
>>> 5 in it1 # Equivalent to an interruption.
True
>>> for p in it1 : print(p)
...
6 # Execution resumes after the interruption.
7
>>>
Iterator may be initialized at any time.
>>> L1 = [1,2,3]
>>> it1 = iter(L1)
>>> next(it1)
1
>>> next(it1)
2
>>> it1 = iter(L1)
>>> next(it1)
1
>>> next(it1)
2
>>> next(it1)
3
>>> next(it1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
After initialization, original iterable may be changed without affecting original result.
>>> L1 = [1,2,3]
>>> it1 = iter(L1)
>>> next(it1)
1
>>> next(it1)
2
>>> L1 = [1,2,4,5]
>>> next(it1)
3
>>> next(it1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
Examples of iterators:
>>> it1 = iter( range(2) )
>>> next(it1, None) ; next(it1, None) ; next(it1, None) ;
0
1
>>>
>>> it1 = iter( zip('abcd', '1234') )
>>> next(it1, None) ; next(it1, None) ; next(it1, None) ; next(it1, None) ; next(it1, None) ;
('a', '1')
('b', '2')
('c', '3')
('d', '4')
>>>
>>> it1 = iter( re.finditer(r'\w+', ' abc DEF 123 ') )
>>> next(it1, 'None') ; next(it1, 'None') ; next(it1, 'None') ; next(it1, 'None') ; next(it1, 'None') ;
<_sre.SRE_Match object; span=(1, 4), match='abc'>
<_sre.SRE_Match object; span=(5, 8), match='DEF'>
<_sre.SRE_Match object; span=(9, 12), match='123'>
'None'
'None'
>>>
We are familiar with list comprehensions "listcomps":
>>> [ p.upper() for p in ('abc',' d ','xyz') ]
['ABC', ' D ', 'XYZ']
>>>
and also with set comprehensions. Is a set comprehension a "setcomp" ?
>>> {p+1 for p in (1,2,3,4,3,4,5)}
{2, 3, 4, 5, 6}
>>>
A generator expression appears to have the syntax of a tuple comprehension.
>>> line_list = [' line 1\n', 'line 2 \n', ' line 3 \n']
>>>
>>> stripped_iter = (line.strip() for line in line_list) # Syntax of listcomp, but within parentheses '()'.
>>>
>>> stripped_iter
<generator object <genexpr> at 0x101a92620> # generator object derived from generator expression.
>>>
Generator object may be used as iterable.
>>> list(( (line, len(line)) for line in line_list ))
[(' line 1\n', 9), ('line 2 \n', 9), (' line 3 \n', 11)]
>>>
>>> tuple(( (line, len(line)) for line in line_list ))
((' line 1\n', 9), ('line 2 \n', 9), (' line 3 \n', 11))
>>>
>>> for p in ( (line, len(line)) for line in line_list ) : print (p)
...
(' line 1\n', 9)
('line 2 \n', 9)
(' line 3 \n', 11)
>>>
Generator object may be used as iterator.
>>> next( stripped_iter )
'line 1'
>>> next( stripped_iter )
'line 2'
>>> next( stripped_iter )
'line 3'
>>> next( stripped_iter )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
As for listcomps, conditions may be added.
>>> stripped_iter = (line.strip() for line in line_list if '2' not in line)
>>> next( stripped_iter, None ) ; next( stripped_iter, None ) ; next( stripped_iter, None ) ;
'line 1'
'line 3'
>>>
>>> stripped_iter = (line.strip().upper() for line in line_list if ('2' in line) or (len(line) >= 10))
>>>
>>> for p in stripped_iter : print (p)
...
LINE 2
LINE 3
>>>
Generators are special functions that simplify the task of writing iterators. Regular functions compute a value and return it, but generators return an iterator that returns a stream of values.
Suppose that the method re.finditer(reg_exp, string) does not exist. You want to create an iterator that will iterate over
string and return all the substrings that match reg_exp .
import re
def words_iter(flag=None) :
start = 0
while True :
m = word.search(s1, start)
if m == None : return
if flag :
yield m
# 'yield' statement identifies this function as
# generator function.
# m is returned to caller.
# Execution of code in function words_iter is suspended.
# All local variables are preserved.
# On next invocation of words_iter, execution resumes
# immediately after 'yield' statement.
else :
yield m[0] # Another 'yield' statement.
start = m.span()[1]
print ("""
s1 = '''
{}'''
""".format(s1)
)
s1 = '''
The quick, brown fox jum......
#####.....#####.....#####.....'''
word = re.compile(r'\w+\s+\w+')
go1 = words_iter(1) # generator object 1, flag supplied.
print ('go1 =', go1, '# generator object derived from function words_iter.')
go1 = <generator object words_iter at 0x1019dfd00> # generator object derived from function words_iter.
<_sre.SRE_Match object; span=(0, 9), match='The quick'>
<_sre.SRE_Match object; span=(11, 20), match='brown fox'>
word = re.compile(r'\w+')
go1 = words_iter() # generator object 1, flag not supplied.
while True :
m = next(go1, None)
if m == None : break
print (m)
The
quick
brown
fox
jum
go1 = words_iter() # generator object 1, flag not supplied.
s2 = "'brown' in go1"
print (
"{}: {}".format( s2,
eval(s2)
)
)
'brown' in go1: True
At times it's convenient to have a generator that provides endless iteration but also terminates on command.
The code below accomplishes this by communicating with the generator via the generator's .close() method.
def counter(count=0):
if not isinstance(count, int) :
exit (99)
while True :
count += 1
status = 0
try:
yield count
except GeneratorExit :
status = 98
except :
status = 97
if status == 97 :
exit (97)
if status == 98 :
return
it = counter(3)
for count in it :
print ( 'count = {}'.format(count) )
if count == 7 :
v = it.close()
print ('v =', v)
count = 4
count = 5
count = 6
count = 7
v = None
Listcomps accept free-format Python. Modify the syntax of the loop
and it fits in a listcomp. The following example mimics str.lstrip() .
s1 = ' abcd 123 '
L2 = [
s1[start:]
for it in (counter(-1),)
for start in it
if s1[start:start+1] != ' '
for v in (it.close(),)
]
print ('L2 =', L2)
L2 = ['abcd 123 ']
|