基础用法
与普通函数不同,迭代器可用于父(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/
领取专属 10元无门槛券
私享最新 技术干货