<jupyter_start><jupyter_text>7 Iterators and Generators   7.1 Iterables, Iterators, and the Iterator Protocol - A `for` loop evaluates an expression to get an *iterable* and then  calls `iter()` to get an iterator.- The iterator's `__next__()` method is called repeatedly until  `StopIteration` is raised.<jupyter_code>for i in 'ab':
    print(i)
iterator = iter('ab')
iterator.__next__()
iterator.__next__()
iterator.__next__()
iterator.__next__()
iterator = iter('ab')
next(iterator)
next(iterator)
next(iterator)<jupyter_output><empty_output><jupyter_text>`next()` just calls `__next__()`, but you can pass it a second argument:<jupyter_code>iterator = iter('ab')
next(iterator, 'z')
next(iterator, 'z')
next(iterator, 'z')
next(iterator, 'z')<jupyter_output><empty_output><jupyter_text>- `iter(foo)`  - checks for `foo.__iter__()` and calls it if it exists  - else checks for `foo.__getitem__()` and returns an object which    calls it starting at zero and handles `IndexError` by raising    `StopIteration`.<jupyter_code>class MyList:
    """Demonstrate the iterator protocol"""
    def __init__(self, sequence):
        self.items = sequence
    
    def __getitem__(self, key):
        print(f'Called __getitem__({key})')
        return self.items[key]
m = MyList('ab')
m.__getitem__(0)
m.__getitem__(1)
m.__getitem__(2)
m[0]
m[1]
m[2]
hasattr(m, '__iter__')
hasattr(m, '__getitem__')
iterator = iter(m)
next(iterator)
next(iterator)
next(iterator)
list(m)
for item in m:
    print(item)<jupyter_output><empty_output><jupyter_text>7.2 Exercises: Iterables, Iterators, and the Iterator Protocol<jupyter_code>m = [1, 2, 3]
it = iter(m)
next(it)
next(it)
next(it)
next(it)
for n in m:
    print(n)
d = {'one': 1, 'two': 2, 'three':3}
it = iter(d)
list(it)
m1 = [2 * i for i in range(3)]
m1
m2 = (2 * i for i in range(3))
m2
list(m2)
list(zip(iter('abcde'), iter('abcde')))
it = iter('abcde')
list(zip(it, it))
it = iter('abcde')
iterators = [it, it]
next(iterators[0])
next(iterators[1])
next(iterators[0])
next(iterators[1])
next(iterators[0])<jupyter_output><empty_output><jupyter_text>7.3 Generator Functions<jupyter_code>def list123():
    print('Before first yield')
    yield 1
    print('Between first and second yield')
    yield 2
    print('Between second and third yield')
    yield 3
    print('After third yield')
list123
list123()
iterator = list123()
next(iterator)
next(iterator)
next(iterator)
next(iterator)
for i in list123():
    print(i)
def even(limit):
    for i in range(0, limit, 2):
        print('Yielding', i)
        yield i
    print('done loop, falling out')
iterator = even(3)
iterator
next(iterator)
next(iterator)
for i in even(3):
    print(i)
list(even(10))<jupyter_output><empty_output><jupyter_text>Compare these versions<jupyter_code>def even_1(limit):
    for i in range(0, limit, 2):
        yield i
def even_2(limit):
    result = []
    for i in range(0, limit, 2):
        result.append(i)
    return result
[i for i in even_1(10)]
[i for i in even_2(10)]
def paragraphs(lines):
    result = ''
    for line in lines:
        if line.strip() == '':
            yield result
            result = ''
        else:
            result += line
    yield result
%%writefile eg.txt
This is some sample
text.  It has a couple
of paragraphs.

Each paragraph has at
least one sentence.

Most paragraphs have
two.
list(paragraphs(open('eg.txt')))
len(list(paragraphs(open('eg.txt'))))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14256737177629672, 'fraction_numerical': 0.01999420457838308, 'mean_word_length': 4.436220472440945, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '606154', 'license': 'BSD-2-Clause', 'n_tokens_mistral': 1264, 'n_tokens_neox': 1224, 'n_words': 349, 'path': 'Python-Epiphanies/Python-Epiphanies-7-Iterators-and-Generators.ipynb', 'repo_name': 'TheCulliganMan/PyCon', 'size': '3451'}