生成器和迭代器
# yield 与 yield from # 我们可以在函数中使用yield来产生值,表面上来看,yield有点像是return,不过函数并不会因为yield而结束,只是将流程控制权交给函数的调用者。 import sys import random def producer(): while True: data=random.randint(0,9) print('生产了:',data) yield data # 产生下一个值,流程回到调用者 def consumer(): while True: data=yield # 调用产生器send()方法时的产生值会成为yield 的运算结果 print('消费了:',data) def clerk(jobs,producer,consumer): print('执行{}次生产与消费'.format(jobs)) p=producer() c=consumer() next(c) # 领消费者执行到yield处 for i in range(jobs): data=next(p) # 获取生产者的生产值 c.send(data) # 将值传给消费者 clerk(int(sys.argv[1]),producer,consumer) # yield可用来创建生成器。 def np_range(n): for i in range(0-n,0): yield i for i in range(1,n+1): yield i print(list(np_range(5))) # 从Python3.4开始,新增了yield from语句,上面的程序片段可以直接改写为以下形式: def np_range(n): yield from range(0-n,0) yield from range(1,n+1) print(list(np_range(5)))
一、迭代器 可迭代对象(Iterable):从语法形式上讲,能调用__iter__方法的数据对象就是可迭代对象: >>> [1,2,3].__iter__() <listiterator object at 0x10221b150> >>> {'name':'alvin'}.__iter__() <dictionary-keyiterator object at 0x1022180a8> >>> {7,8,9}.__iter__() <setiterator object at 0x1021ff9b0> 迭代器对象的特性就是能够调用__next__方法依次计算出迭代器中的下一个值。 >>> s={1,2,3} >>> i=s.__iter__() # 返回可迭代对象s的迭代器对象i >>> i.__next__() # 从第一个元素开始,i通过__next__方法就可以得到可迭代对象s的下一个值。 1 >>> i.__next__() 2 >>> i.__next__() 3 >>> i.__next__() #迭代结束,没有下一个值时调用__next__()抛出StopIteration的异常 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 提示:可以用iter(s)取代s._iter__(),其实iter(s)本质就是在调用s.__iter__(),这与len(s)会调用s.__len__()是一个原理,同理,可以用next(i)取代i.__next__()。obj.__iter__()方法的调用后返回的就是一个迭代器对象 于是我们我们可以将之前基于索引的迭代换成迭代器的形式,如下: #while的形式 l=['a','b','c','d','e'] i=iter(l) while 1: try: print(next(i)) except StopIteration: break 迭代器的优点:惰性计算,python中的Iterator对象只是在需要时才去不断调用next()来计算一个个值,需要时,临时计算出,之前是不存在的。就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流 迭代器由于对内存存储的优化在py3中已经大量使用,我们可以通过isinstance()判断一个对象是否是Iterable和Iterator: >>> from collections import Iterable,Iterator >>> isinstance([1,2,3],Iterable) True >>> i=iter('abc') >>> isinstance(i,Iterator) True 迭代器协议要求迭代对象具有__iter__()和__next__()两个方法,__iter__则是返回迭代器本身,目的是使for循环可以遍历迭代器对象 二生成器:简单说,生成器就是使用了yield关键字的函数: >>> def countdown(n): ... print('countdown start') ... while n > 0: ... yield n ... n-=1 ... print('Done!') >>> countdown <function countdown at 0x102212f50> >>> countdown(5) <generator object countdown at 0x1021ff9b0> >>> gen=countdown(3) # 验证对象是否为迭代器对象 >>> gen <generator object countdown at 0x101be0a40> >>> from collections import Iterator >>> isinstance(gen,Iterator) True >>> gen=countdown(3) >>> gen.__next__() countdown start 3 >>> gen.__next__() 2 >>> gen.__next__() 1 >>> gen.__next__() Done! Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> c=countdown(7) >>> c <generator object countdown at 0x10123f308> >>> next(c) countdown start 7 >>> next(c) 6 >>> c.close() >>> next(c) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration yield的功能总结: (1)封装iter和next方法 (2)执行函数时遇到yield返回其后的值,不同于return,yiled可以返回多次值 (3)挂起函数的状态,等待下一次调用next方法时找到对应的暂停位置继续执行。 创建一个生成器对象有两种方式,一是通过在函数中创建yield关键字来实现。另一种就是生成器表达式,这是一种类似于数据类型中学过的列表生成式的语法格式,只是将[]换成(),即: (expression for item in iterable if condition) 不同于列表生成式最后返回一个列表结果,生成器表达式顾名思义会返回一个生成器对象,比如: >>> [x*x for x in range(4)] # [0, 1, 4, 9] >>> gen=(x*x for x in range(4)) >>> gen <generator object <genexpr> at 0x101be0ba0> 当需要用到其中的值时,再通过调用next方法或者for循环将值一个个地计算出来: >>> next(gen) 0 >>> next(gen) 1 >>> next(gen) 4 >>> next(gen) 9 >>> next(gen) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration