2.1. 迭代器与生成器

2.1.1. 手动遍历迭代器

In [5]: a = iter(range(1,3))

In [6]: next(a)
Out[6]: 1

In [7]: next(a)
Out[7]: 2

In [8]: next(a)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[8], line 1
----> 1 next(a)

StopIteration:

In [10]: b=next(a,None)

In [11]: b

In [12]: b is None
Out[12]: True

2.1.2. 代理迭代

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

    def __repr__(self):
        return f'Node({self._value})'

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

    def __iter__(self):
        return iter(self._children)
    
if __name__ == "__main__":
    root = Node(0)
    child1 = Node(3)
    child2 = Node(2)
    root.add_child(child1)
    root.add_child(child2)
    for ch in root:
        print(ch)

2.1.3. 使用生成器创建新的迭代模式

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


def countdown(n):
    print('start ')
    while n>0:
        yield n 
        n-=1
    print('done')


if __name__ == "__main__":
    for n in frange(0, 4, 0.5):
        print(n)

    
# symbol: frange
# hover info: (function) def frange(
#     start: Any,
#     stop: Any

2.1.4. 实现迭代器协议

class Node(object): 
    def __init__(self,value) -> None:
        self._value = value
        self._child = [ ]
    
    def __repr__(self) -> str:
        return 'Node({!r})'.format(self._value)

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

    def __iter__(self):
        return iter(self._child)
    def depth_first(self):
        yield self
        for c in self:
            yield from c.depth_first()
    # def width_first(self):
    #     # yield self
    #     yield from self._child
    #     for c in self:
    #         yield from c.width_first()

class NodeV2(object):
    def __init__(self,value) -> None:
        self._value = value
        self._child = [ ]
    
    def __repr__(self) -> str:
        return 'Node({})'.format(self._value)

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

    def __iter__(self):
        return iter(self._child)
    def depth_first(self):
        return DepthFirstIterator(self)
    
# todo DepDepthFirstIterator


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

#            0
#         1      2
#       3  4   5
# 
    for ch in root.depth_first():
        print(ch)
    print('-'*20)
    # for ch in root.width_first():
    #     print(ch)

    

2.1.5. 反向迭代

使用内置的 reversed() 函数 。

class Countdown(object):
    def __init__(self, start):
        self.start = start

    # Forward iterator
    def __iter__(self):
        n = self.start
        while n > 0:
            yield n
            n -= 1

    # Reverse iterator
    def __reversed__(self):
        n = 1
        while n <= self.start:
            yield n
            n += 1

for r in reversed(Countdown(30)):
    print(r)

2.1.6. 带有外部状态的生成器函数

from collections import deque
class linehistory(object):
    def __init__(self,lines,max_size) -> None:
        self.lines = lines 
        self.max_size = max_size
        self.history = deque(maxlen=self.max_size)
    
    def __iter__(self):
        for lineno,line in enumerate(self.lines,1):
            self.history.append((lineno,line))
            yield line 
        
    def clear(self):
        self.history.clear()

if __name__ == "__main__":
    with open('./somefile.txt') as f:
        lines = linehistory(f,3)
        for line in lines:
            if 'python' in line:
                for lineno,hline in lines.history:
                    print(f'{lineno}:{hline}')

2.1.7. 迭代器切片

def countdown(n):
    print('start ')
    while n>0:
        yield n 
        n-=1
    print('done')


if __name__ == "__main__":
    c = countdown(10)
    # c[0:3]

    import itertools
    res = itertools.islice(c,0,3)
    for i in res:
        print(i)

2.1.8. 跳过可迭代对象的开始部分

def countdown(n):
    print('start ')
    while n>0:
        yield n 
        n-=1
    print('done')


from itertools import dropwhile
res = dropwhile(lambda n:n>=5,countdown(10))
for i in res:
    print(i)

2.1.9. 排列组合的迭代

  • 排列: itertools.permutations()

  • 组合: itertools.combinations()

  • 带重复: itertools.combinations_with_replacement()

In [1]: items = ['a', 'b', 'c']

In [2]: import itertools

In [3]: list(itertools.combinations(items,2))
Out[3]: [('a', 'b'), ('a', 'c'), ('b', 'c')]

In [4]: list(itertools.combinations_with_replacement(items,2))
Out[4]: [('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'b'), ('b', 'c'), ('c', 'c')]

In [5]: list(itertools.permutations(items,2))
Out[5]: [('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'c'), ('c', 'a'), ('c', 'b')]

2.1.10. 序列上索引值迭代

In [6]: a=['a','b','c']

In [7]: for lineno,item in enumerate(a,1):
...:     print(lineno,item)
...:
1 a
2 b
3 c

2.1.11. 同时迭代多个序列

In [8]: l1 = ['a','b','c']

In [9]: l2 = ['A','B','C']

In [10]: l3 = [1,2,3]

In [12]: for i in zip(l1,l2,l3):
    ...:     print(i)
    ...:
('a', 'A', 1)
('b', 'B', 2)
('c', 'C', 3)

In [13]: dict(zip(l1,l2))
Out[13]: {'a': 'A', 'b': 'B', 'c': 'C'}

In [14]: for a,b in zip(l1,l2):
    ...:     print(a,b)
    ...:
a A
b B
c C

2.1.12. 不同集合上元素的迭代

# Inefficent
for x in a + b:
    ...

# Better
for x in chain(a, b):

2.1.13. 创建数据处理管道

todo

2.1.14. 展开嵌套的序列

from collections.abc import Iterable

def flatten(item,ignore_types=(str,bytes)):
    for x in item:
        if isinstance(x,ignore_types):
            yield x
        elif  isinstance(x, Iterable) :
            yield from flatten(x)
        else: 
            yield x 

items = [1, 2, [3, 4, [5, 6], 7], 8,'abc']
print(list(flatten(item=items)))
    

2.1.15. 顺序迭代合并后的排序迭代对象

In [1]: a=[1,3,5,7]

In [2]: b=[0,2,4,8,6]

In [3]:  import heapq

In [5]: list(heapq.merge(a,b))
Out[5]: [0, 1, 2, 3, 4, 5, 7, 8, 6]