一句话总结generator:generator允许循环得到一串数据,且如何得到数据的过程可以自定义
以下对于generator的描述都是正确的:
1)generator以对计算资源的消耗取代了对内存的消耗
2)generator有用的地方在于允许自定义得到感兴趣数据的过程
3)因为返回的可迭代对象,所以可以方便的使用循环
4)generator坚持从程序运行的大局出发,在自己执行的过程中主动交出程序控制流,方便了用户得到所需的数据,堪称雷锋
如何理解这几点呢?
从最近自己的一个编码经历说起。
最近接到一个任务,要把两个输入队列拼装为一个输出队列。一个输入队列是IP段,每个元素要么是单个IP,要么是一个IP段(例如10.10.10.1-10.10.10.254),另一个输入队列是端口号,每个元素要么是单个端口,要么是一段端口号(例如1-8000),要求输出队列的元素是IP和端口号的结合,格式是IP:PORT,例如10.10.10.5:23,并且要包括每一个输入的IP和端口号的结合,例如如果输入了10个IP,65535个端口,那么输出队列就要包含这十个IP的所有65535个端口,共65万个元素!
这还仅仅是10个IP,输入的IP完全可能是一个B类地址,共65535个IP,乘以65535个端口,输出队列共约43亿个元素!如果我把输出队列放到一个list里面,很可能还没初始化完这个list,内存已经被占用完了。
咋办呢?
当我为这个问题发愁的时候,我已经把generator那篇翻译的文章完成有一段时间了,显然翻译完还是没有理解generator精髓的,要在经历过一个失眠的夜晚后才真正理解generator存在的价值。
当天晚上躺在床上一直在考虑这个问题,在回忆译文中的例子的时候意识到generator正是解决我面临问题的最佳办法。
generator看起来像是个函数,函数就是处理逻辑,generator的处理逻辑就是你想怎么处理输入数据,以便得到你想要的数据,对想要的数据yield即可。我的输入数据显然就是两个输入队列,取它们的每一个元素,对它们进行拼接,就得到了一个我想要的元素,在这个元素钱添加yield,这个generator就完成了我想要的功能。
下面是我定义的generator,其实genip()和genport()也是generator
def genipport(iplist, portlist):
IPs = genip(iplist)
for ip in IPs:
Ports = genport(portlist)
for port in Ports:
yield (ip,port)
下面是我使用generator的代码:
ipWITHport = genipport(IPList, PortList)
item = next(ipWITHport)
经过验证,程序运行的效果非常好,相比于使用list存放输出队列这种最原始的方法来说,generator简直几乎就是不占用内存,它是在运行过程中每获取到一个所需元素后(执行到yield语句)就将程序控制流转移到调用方执行,调用方请求下一个的时候(例如调用next函数)再把程序控制流转移到generator内部,因为程序控制流转出的时候generator会保留当前的环境和变量值,所以程序控制流转移回来的时候会继续从上次已经获取的元素处继续运行,取下一个元素,就好像python是个导演,在generator执行的时候导演随时会喊cut,直到所有的元素被取出为止。
***************************************************************************
欢迎扫码关注我的微信公众号zhixin991