生成器和迭代器
一,迭代器(iterator)和可迭代对象(iterable)
1.两者的介绍:
迭代器:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个Stopiteration异常,以终止迭代(只能往前走不能后退)
可迭代对象:实现了迭代器协议的对象(实现:对象内部定义一个_iter_()方法)
注:字符串、列表、元组、字典、集合、文件对象这些都不是可迭代对象,只不过for循环调用了他们内部的_iter_方法,把他们变成了可迭代对象。
例:可迭代对象的使用方法: a = [1,2,3] iter_a = a.__iter__() print(iter_a.__next__()) print(iter_a.__next__()) print(iter_a.__next__()) 注: print(next(iter_a)) = print(iter_a.__next__()) 一个调用python提供的内置函数,一个调用数据类型提供的方法 例:用while去模拟for循环: a = [1,2,3,5] iter_a = a.__iter__() while True: try: print(iter_a.__next__()) except StopIteration: break
2.迭代器和可迭代对象的判断
可以使用isinstance()
判断
>>> from collections import Iterable >>> isinstance([], Iterable) True >>> isinstance({}, Iterable) True >>> isinstance('abc', Iterable) True >>> isinstance((x for x in range(10)), Iterable) True >>> isinstance(100, Iterable) False >>> from collections import Iterator >>> isinstance((x for x in range(10)), Iterator) True >>> isinstance([], Iterator) False >>> isinstance({}, Iterator) False >>> isinstance('abc', Iterator) False
笔者自己画的图,他们之间的关系如图所示
小结:
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;
集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
Python的for
循环本质上就是通过不断调用next()
函数实现的.
二、生成器
1.什么是生成器:
可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法),所以生成器就是可迭代对象。
2.生成器分类:
1):生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个据俄国中间,挂起函数的状态,以便下次从他离开的地方继续执行.
2): 生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构造一个结果列表。
3.生成器特点:
1).获得返回值yield(类似函数return)
2).保留函数的运行状态,下一次运行相当于执行__next()__方法
4.生成器函数总结:
1).语法上和函数类似:生成器函数和常规函数几乎是一样的。它们都是使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值。
2).自动实现迭代器协议:对于生成器,Python会自动实现迭代器协议,以便应用到迭代背景中(如for循环、sum函数)。由于生成器自动实现了迭代器协议,所以,我们可以调用你它next方法,并且,在没有值可以返回的时候,生成器自动产生StopIteration异常
3).挂起状态:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行
#--------包子案例:商家做包子,客人吃包子 import time def product_baozi(): for i in range(100): yield '做好了包子%s'%i time.sleep(3) baozi = product_baozi() names = ['aaa','bbb','ccc','ddd'] for name in names: s = baozi.__next__() time.sleep(1) print( s[3:],'已被%s吃了'%name)
*附加:生成器只能遍历一次!
示例:
def test():
for i in range(4):
yield i
n = test()
n1 =(i for i in n)
n2 =(i for i in n1)
print(list(n2))
输出结果:
[0, 1, 2, 3]
******************
def test():
for i in range(4):
yield i
n = test()
n1 =(i for i in n)
n2 =(i for i in n1)
print(list(n1))
print(list(n2))
输出结果:
[0, 1, 2, 3]
[]
5.生成器的send用法
def test(): print('start') num = yield 1 yield 2,num yield 3 a = test() test0 = a.__next__() test1 = a.send(24) #send的作用相当于使生成器继续运行,并且传递的参数为yield的返回值 test2 = a.__next__() print(test0) print(test1) print(test2)
6.生产者和消费者模型
通过yield实现在单线程的情况下实现并发运算的效果
import time def producer(name): while True: baozi = yield time.sleep(1) print('%s已制作完毕'%baozi) print('%s把%s吃了'%(name,baozi)) print('-'*30) def consumption(): People_eat1 = producer('Jiang') People_eat2 = producer('Feng') People_eat1.__next__() People_eat2.__next__() for i in range(10): if (i+1)//2 ==1: People_eat1.send('包子%s'%i) else: People_eat2.send('包子%s'%i) consumption()