Python cookbook iterator and generator

1手动遍历迭代器

想遍历一个可以迭代对象中的所有元素,但是却不想使用for循环

使用next()函数并在代码中捕获StopIteration异常,迭代期间的基本细节.

def manual_iteration():
    with open('cookie.txt') as f:
        try:
            while True:
                line=next(f)
                print(line,end='')
        except StopIteration:
            pass
manual_iteration()

2代理迭代

问题:构建了一个自定义容器,里面包括列表,元祖和其他可迭代对象,希望直接在新容器对象上执行迭代操作

# 重构__repr__方法后,不管直接输出对象还是通过print打印的信息都按我们__repr__方法中定义的格式进行显示了
# 你会发现,直接输出对象时并没有按我们__str__方法中定义的格式进行输出,而用print输出的信息却改变
class Node:
    def __init__(self,name):
        self._name=name
        self._children=[]
    def __repr__(self):
        return 'Node({})'.format(self._name)
    def add_child(self,node):
        self._children.append(node)
    def __iter__(self):
        return iter(self._children)
node1=Node(1)
node=Node(0)
node2=Node(2)
node.add_child(node1)
node.add_child(node2)
for ch in node:
    print(ch)了

解决方案:定义一个__iter__()方法,将迭代操作代理到容器内部的对象上去,这里的iter函数的使用简化了代码,iter(s)只是简单地通过s.__iter__()方法来返回对应的迭代器对象,就跟len(s)会调用s.__len__()原理是一样的

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

问题:自定义迭代模式...跟range() reversed()不一样

def frange(start,stop,step):
    x=start
    while x<stop:
        yield x
        x+=step
for i in frange(1,10,2):
    print(i)

一个函数需要有一个yield语句既可以将其转换成生成器,生成器只能用于迭代操作

一个生成器函数主要特征是它只会回应在迭代中使用到的next操作,一旦生成器函数返回退出,迭代终止..

def frange(start,stop,step):
    x=start
    while x<stop:
        x+=step
        return x
for i in frange(1,10,2):
    print(i)

也就是说yield就免去了自己创建一个list的操作,也不需要多占用内存

4实现迭代器协议

问题:支持迭代操作的自定义对象

深度优先遍历属性节点的生成器

#生成树形节点
class Node:
    def __init__(self,name):
        self._name=name
        self._children=[]
    def __repr__(self):
        return 'Node({})'.format(self._name)
    def add_child(self,node):
        self._children.append(node)
    def __iter__(self):
        return iter(self._children)
    def depth_first(self):
        yield self
        for i in self._children:
            yield from i.depth_first()
node1=Node(1)
root=Node(0)
node2=Node(2)
root.add_child(node1)
root.add_child(node2)
node1.add_child(Node(3))
node1.add_child(Node(4))
node2.add_child(Node(5))
for ch in root.depth_first():
    print(ch)

5反向迭代一个序列

使用内置的reversed()函数.也可以通过在自定义类上实现反向迭代

class CountDown:
    def __init__(self,start):
        self.start=start
    def __iter__(self):
        n=self.start
        while n>0:
            yield n
            n-=1
    def __reversed__(self):
        n=1
        while n<=self.start:
            yield n
            n+=1
for i in CountDown(3):
    print(i)

for i in reversed(range(3)):
    print(i)

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

问题:你想定义一个生成器函数,但是它会调用某个你想暴露给用户的外部状态值

解决方案:如果你想让你的生成器暴露外部状态给用户,可以简单就将其生成一个类,然后把生成器函数放到__iter__()方法中过去

from collections import deque
class linehistory():
    def __init__(self,lines,histlen=3):
        self.lines=lines
        self.history=deque(maxlen=histlen)
    def __iter__(self):
        for lineno,line in enumerate(self.lines):
            self.history.append(line)
            yield line
    def clear(self):
        self.history.clear()
        
with open('cookie.txt') as f:
    lines=linehistory(f)
    print(lines)
    for line in lines:
        print(line)
想暴露谁yield谁....其他的不可见

7迭代器切片

函数itertools.islice()正好适用于迭代器和生成器上做切片操作

def count(n):
    while n<20:
        yield n
        n+=1
c=count(0)
import itertools
for x in itertools.islice(c,10,20):
    print(x)

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

itertools模块中有一些函数可以完成,itertools.dropwhile(),传递一个函数对象和一个可迭代对象,丢弃函数返回True的原有序列

from itertools import dropwhile
li=['#a','#b','#1','2']
for i in dropwhile(lambda line:line.startswith('#'),li):
    print(i,end='')
#明确知道哪个位置不想要可以用islice(item,index,None)

9排列组合的迭代

迭代遍历一个集合中元素的所有可能的排列或组合

itertools.permutations()接收一个集合并产生一个元组序列,每个元组由集合中所有元素的一个排列组成,也就是说通过打乱集合中元素排列顺序生成一个元组

from itertools import permutations
for i in permutations(li):
    print(i)

for i in permutations(li,2):
    print(i)#指定长度的序列

itertools.combinations 输入集合中元素的所有组合
对于combinations来讲,元素的顺序已经不重要了,('a','b')和('b','a')等价

itertools.combinations_with_replacement()允许同一个元素被选取多次

10序列上索引值迭代

迭代一个序列的同时跟踪正在被处理的元素索引

内置的enumerate

11同时迭代多个序列

想同时迭代多个序列,每次分别从一个序列中取一个元素

为了同时迭代多个序列,使用zip函数

x=[1,2,3,4,5]
y=[1,2,445,56]
for m,n in zip(x,y):
    print(m,n)

zip(a,b)会产生一个可返回元组(x,y)的迭代器,一旦某个序列到底结尾,迭代宣告结束,因此迭代长度跟参数中最短序列长度一致

itertools.zip_longest()函数 匹配最长的序列长度  zip zip_longest()都可以添加fillvalue=0这个参数

12不同集合上元素的迭代

不同容器中的对象的避免重复的迭代

itertools.chain() 方法可以简化这个任务,接受一个可迭代对象作为输入,并返回一个迭代器,有效的屏蔽掉在多个容器中迭代细节

from itertools import chain
for i in chain(x,y):
    print(i)

使用chain()的一个常见场景是当你想对不同几个中所有元素执行操作的时候,将集合放到set里面去..

itertools.chain()接受一个或者多个可迭代对象作为输入参数,然后创建一个迭代器,依次连续的返回每个可迭代对象中的元素

13创建数据处理管道(150)

问题:以数据管道(类似Unix管道)的方式迭代处理数据,比如,有个大量的数据需要处理,但是你不能将其一次性放入内存里面

解决方案:定义一个由多个特定任务独立任务的简单生成器组成的容器

14展开嵌套的序列 yield from 相当于 for item in items:yield item 就是返回迭代器里面全部的内容的意思

from collections import Iterable
def flattern(items,ignore_type=(str,bytes)):
    for item in items:
        if isinstance(item,Iterable) and not isinstance(item,ignore_type):
            yield from  flattern(item)
        else:
            yield item
items=[1,2,[3,4,[5,6],7],8]
for x in flattern(items):
    print(x)

yield from 在涉及到基于协程和生成器的并发编程中扮演着更加重要的角色

15顺序迭代合并后的排序迭代对象(排序好的)

heapq.merge()函数可以解决

import heapq
x=[1,2,3,4,5]
y=[1,2,445,56]
for i in heapq.merge(x,y):
    print(i)

16迭代器代替while无限循环

需要调用某个函数或者一般迭代模式不同的测试条件

def reader(s):
    while True:
        data=s.recv(CHUNKSIZE)
        if data==b'':
            break
        process_data(data)
def reader(s):
    for chunk in iter(lambda: s.recv(CHUNKSIZE),b''):
        pass
    #process_data(data)

iter函数有一个特性就是它接受一个可选的callable对象和一个标记(结尾)值作为输入参数,当以这种方式使用的时候,它会创建一个迭代器,这个迭代器会不断调用callable对象直到返回值和标记值相等为止

猜你喜欢

转载自blog.csdn.net/qq_37312720/article/details/84578453