迭代器与生成器
迭代器
1.迭代器协议:对象必须提供一个next方法,执行这个方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以总结迭代.(迭代是只能往后不能往前)
2.可迭代对象:实现了迭代器协议的对象(实现方式是在对象内部定义一个__iter__()方法)
3.协议是一种约定,可迭代对象实现了对迭代器协议,python的内部工具使用迭代器协议访问对象如(for循环 max() min() sun()函数等)
for循环本质是循环所有对象,全部使用迭代器协议,for循环可以遍历:字符串\列表\元组\字典\集合\文件对象
但是如列表没有next方式 for循环是如何实现的 具体实现方法如下
正常的for循环
l = [1,2,3,4,5]
for i in l: #l----> i_l=l.__iter__() i---->i_l.__next__()
print(i)
输出结果
1
2
3
4
5
上述的for循环是进行上面的操作,然后捕捉到StopIteration结束当前操作 如下
字典 集合 文件对象是非序列类型
#字典
dic={'a':1,'b':2}
for item in dic:
print(item)
#for 循环字典 打印的是key值,
iter_itme=dic.__iter__()
print(iter_itme.__next__()) #这个打印出的 a 字典的key值
输出结果
a
b
a
文件示例
f =open('a.txt','r+',encoding='utf-8')
#for i in f: #iter_f=f.__iter__()
# print(i)
iter_f=f.__iter__()
print(iter_f.__next__())
print(iter_f.__next__())
#这样输出的内容中间会有换行 在后面加end=''没有了
print(iter_f.__next__(),end='')
print(iter_f.__next__(),end='')
下面是用while循环模拟for循环中的迭代器协议
l = [1,2,3,4,5]
iter_l=l.__iter__()
while True:
try:
print(iter_l.__next__())
except StopIteration:
print('迭代结束')
break
生成器
生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法,但是生成器可直接.nest,不需要先调用自己内置的__iter__方法),所以生成器也是可迭代对象
1.生成器函数:常规函数定义但是使用yield 结束语句,而不是return语句返回结果,yield语句一次返回一个结果,每个结果中间,挂起函数的状态,以便下次从他离开的地方继续执行
2.生成器表达式:类似于列表推导,但是生成器返回按需产生结果的一个对象,饿不是一次构建一个结果列表
列表生成器
方式一:
#列表解析 缺点是处理大量数据的时候占用内存量大
l =[]
for i in range(10):
l.append('鸡蛋%s'%i)
print(l)
输出结果
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
上面也可以写成如下形式
l = ['鸡蛋%s'%i for i in range(10)]
print(l)
写成生成器的方式
laomuji = ('鸡蛋%s'%i for i in range(10))
print(laomuji)
print(laomuji.__next__())
输出结果
<generator object <genexpr> at 0x00000193D2E245E8>
鸡蛋0
如果要一个个打印出来,可以通过next()函数获得generator的下一个返回值:
print(laomuji.__next__())
print(laomuji.__next__())
print(laomuji.__next__())
print(laomuji.__next__())
print(laomuji.__next__())
print(laomuji.__next__())
yield用法
yield 一旦被采用,那么def后面的代码会立马被认作是一个 generator 而不是一个 function,因为生成的是一个 generator
def test():
print("第一")
yield 1
print('第二')
yield 2
t=test()
res=t.__next__()
print(res)
print(t.send(None))
上面的send(None)和__next__()作用相同 但可以传值
yield相当于return控制函数的返回值,且它也可以接受send传过来的值
def test():
print('开始了')
first = yield #yield 相当月return 后面跟值 没有值默认为None
print('第一次',first)
yield 2
print('第二次')
t = test()
res = t.__next__()
print(res)
res = t.send('函数停留在first这个位置,就是给first赋值的')
print(res)
输出结果
开始了
None
第一次 函数停留在first这个位置,就是给first赋值的
2
程序运行到 first = yield时暂停,且记录该位置,等执行res = t.send(‘函数停留在first这个位置,就是给first赋值的’)时,从上次记录的位置及first = yield开始,把send中的值传给yield send---->yield---->first 及first的值就是***(‘函数停留在first这个位置,就是给first赋值的’)***
import time
def consumer(name):
print('我是[%s],我要开始吃包子了' %name)
while True:
baozi=yield
time.sleep(1)
print('%s 很开心的吧【%s】吃掉了' %(name,baozi))
def producer():
c1=consumer('xxxx1')
c2=consumer('xxxx2')
c1.__next__()
c2.__next__()
for i in range(10):
time.sleep(1)
c1.send('包子%s' %i)
c2.send('包子%s' %i)
producer()
运行producer() 在producer中又运行了consumer(即c1=consumer(‘xxxx1’)和c2=consumer(‘xxxx2’)),consumer中有yield
所以def consumer是一个生成器对象所以需要next()方法(即c1.next()和c2.next()),下面就是循环10次循环10次的值用send传入