Python语言特性:迭代器用法(一)

基础用法

与普通函数不同,迭代器可用于父(caller)-子(callee)之间的来回切换,通过迭代器可在子函数返回数据给父函数后,父函数又将函数控制权交给子函数。在递交控制权的同时还可以传输参数,而这些控制权的来回变动和传参均与函数签名无关。

容器遍历

最常见的实例为容器的遍历,也就是实现for x in list的功能,list因使用了迭代器功能才能支持for-loop语法,注意,迭代器函数不支持返回值。

#for x in test_list()

def test_list():

print(“hello”)

l = [random.randint(1,100000) for x inrange(4)]

for _,v in enumerate(l):

yield v

带有迭代器的函数会自动产生一个迭代器对象generator,该迭代器对象只有调用next函数时才执行程序,直至出现StopIteration异常为止。

>>>from test_list import test_list

>>>x = test_list()

>>>next(x)

hello

80593

>>>next(x)

99561

>>>next(x)

79218

>>>next(x)

Traceback(most recent call last):

File "", line 1, in

StopIteration:0

2. 父子交互

python版迭代器只支持父(函数)-子(迭代器)一级调用间的数据传递,而其他语言如lua可支持多级调用间的来回切换。这种来回切换且传递参数称之为”协程”,因来回切换可实现运行控制权的移交,这种机制可实现线程弱一级的功能,很多库因此利用该功能来代替线程实现高性能网络库,避免了线程间的切换,降低开销,如:Twisted,Greenlet,所有阻塞事件通过多路复用函数select/epoll来复用为最终一个阻塞事件,其余均通过yield来实现基于Task的非阻塞事件处理。

def writer2():

while True:

try:

w = (yield)

except SpamException:

print('****')

exceptGeneratorExit:

print('****')

else:

print('>>',w)

函数write2通过w来接收父函数传递的参数,经过处理后,输出结果。父函数通过广播所有子函数来实现任务的分解处理,且不存在多线程中的同步问题。注意,首次发送必须是None,或子迭代器调用next函数来初始化迭代器。通过迭代器可以一级一级的实现数据过滤,获取最终结果,这也是C#中的LINQ原理。

wp =writer2()

wp.send(None)

for i inrange(4):

if i == 3:

wp.throw(SpamException)

else:

wp.send(i)

wp.close()#显式关闭迭代器抛出GeneratorExit用于子迭代器清理资源

高阶用法

管道与过滤器

管道用于将数据进行输入/输出串起来,过滤器就是在这串联的过程中实现数据的层层过滤、转换来获取最终的数据,与Linux中管道不同的是,这里传递的是迭代器非字符串。

实现该模式注意事项如下:

输入部分必须为迭代器,每个处理函数都是作为迭代器作为参数,返回迭代器;

最后一个函数可输出迭代器或进行规约。

一个简单的实例代码如下:

import random

def sum(items):

d= 0

for data in items:

d = data + d

return d

def process(items):

for item in items:

_,num = item

yield num

def gen():

for i in range(10):

num = random.randint(1,1000)

print("gen random int:%s" % (num))

yield (i,num)

if __name__ == "__main__":

print("sum:%s" %sum(process(gen())))

协程

协程作为网络通讯库greenlet/twisted的实现基础,通过协程可实现线程内部的任务切换,减少系统调度开销。主节点通过发送数据至子节点实现唤醒。主节点需要实现任务调度器来调动子节点。

一个文件查找的功能采用该框架模式实例如下:

#coding=utf-8

from collections import deque

import sys

def grep(pattern):

while True:

#print("waiting match pattern:%s" % (pattern))

line = (yield)

if pattern in line:

print(line)

def runner(path,items):

tasks =deque(items)

#唤醒迭代器

for item in items:

next(item)

#print("wake up item.")

with open(path) as f:

content = f.readline()

while content:

#print("content:%s" % (content))

task = tasks.pop()

task.send(content)

tasks.appendleft(task)

content = f.readline()

for item in items:

item.close()

if __name__ == "__main__":

items = [grep(sys.argv[2]),grep(sys.argv[2]),grep(sys.argv[2])]

runner(sys.argv[1],items)

该模式可总结如下:

子任务通过yield实现阻塞,调度器通过发送数据唤醒子任务;

子任务不能有阻塞任务,否则会引起整个协程阻塞;

异步IO采用协程模式采用多路复用将所有子任务阻塞在调度器中,通过找到对应子任务,恢复其执行。

参考资料:

https://www.dabeaz.com/coroutines/Coroutines.pdf

http://python.jobbole.com/85117/

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20180603G1E0K900?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券