迭代器and生成器
主要内容:
1,迭代器
2,生成器
一,迭代器
之前一直在用可迭代对象进行迭代操作,那么到底什么是可迭代对象
str,list,tuple,dict,set.那么为什么我们可以称他们为可迭代对象呢?因为他们都遵循了可迭代协议,什么事可迭代协议.
s = 'abc'
for c in s:
print(c)
for i in 123:
print(i) # 'int' object is not iterable
注意:报错信息,整数类型对象不是可迭代的,iterable表示可迭代的,表示可迭代协议,那么如何进行验证数据类型是否符合可迭代协议.我们可以通过dir()函数进行产看类中定义好的所有方法
s = '我的哈哈哈哈' # print(dir(s)) print(dir(str)) ''' ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']'''
list,tuple,dict,set同样,自行查看
综上,我们可以确定,如果对象中有__iter__函数,那么我们认为这个对象遵守了可迭代协议,就可以进行迭代,这里的__iter__shi帮助我们获取到对象的迭代器,我们使用__next__()来获取到一个迭代器中的元素,那么我们之前讲的for的工作原理是什么呢?
s = '我爱我的祖国' c = s.__iter__() print(c.__next__()) print(c.__next__()) print(c.__next__()) print(c.__next__()) print(c.__next__()) print(c.__next__()) # 这个是最后一个 # print(c.__next__()) # StopIteration
我们可以把迭代的内容当成子弹,然后呢,获取到迭代器__iter__(),就把子弹都装在弹夹中,然后发射就是__next__()把每一个子弹(元素)打出来,也就是说,for循环的时候,一还是的时候是__iter__()来获取迭代器,后面每次获取元素都是通过__next__()来完成的.当程序遇到StopIterable将结束循环
二,生成器
什么事生成器,生成器实质就是迭代器
在python中有三种方式来获取生成器
1,通过生成器函数
2,通过各种推导式来实现生成器
3,通过数据的转换也可以获取生成器
def func():
print("111")
return 222
ret = func()
print(ret)
# 结果
# 111
# 222
将函数中的return替换成yield就是生成器
def func():
print("111")
yield 222
ret = func()
print(ret)
# 结果:<generator object func at 0x000001F05A470E08>
运行的结果和上面的不一样,为什么呢?由于函数中存在了yield.那么这个函数就是一个生成器函数.这个时候,我们在执行这个函数的时候.就不再是函数的执行了.而是获取这个生成器.如何使用?想想迭代器.生成的本质就是迭代器,所以,我们可以直接运行__next__()来执行以下生成期:
def func():
print("111")
yield 222
gener = func() # 这个时候函数不会执行,而是获取到生成器
ret = gener.__next__() # 这个时候函数才会执行,yield的作用和return一样,就是返回数据
print(ret)
# 结果:
# 111
# 222
那么我们可以看到.yield和return的效果是一样的,有什么去别呢?yield是分段来执行一个函数.return呢?直接停止执行函数
def func():
print("111")
yield 222
print("333")
yield 444
gener = func()
ret = gener.__next__()
print(ret)
ret2 = gener.__next__()
print(ret2)
ret3 = gener.__next__() # 这个会报错
print(ret3)
当程序运行到最后一个yield.那么后面继续进行__next__()程序会报错.
好了生成器说晚了.生成器有什么作用呢?我们来看这样一个需求.学校想耐克订单100000件校服,要是等衣服制造好了,全部一次性交付,学生都毕业了.
def cloth():
for i in range(0, 1000):
yield '衣服' + str(i)
c1 = cloth()
print(c1.__next__())
print(c1.__next__())
print(c1.__next__())
print(c1.__next__())
# 一次性提取50件衣服
for i in range(0, 50):
ret = c1.__next__()
print(ret)
接下来是send方法,send和__next__()方法都可以让生成器执行到下一个yield.
def eat():
print('我吃什么啊') # 运行第一个yield的时候,会执行print(),并将"馒头"返回给ret1,进而打印ret1
a = yield '馒头'
print(a) # 当执行第二个yield的时候,yield会返回一个None赋值给a,打印a就是一个None
b = yield '大饼'
print(b)
c = yield '韭菜盒子'
print(c)
print('程序结束')
yield 'Game Over'
gen = eat()
ret1 = gen.__next__()
print(ret1)
ret2 = gen.__next__()
print(ret2)
ret3 = gen.__next__()
print(ret3)
ret4 = gen.__next__()
print(ret4)
# 结果
# 我吃什么啊
# 馒头
# None
# 大饼
# None
# 韭菜盒子
# None
# 程序结束
# Game Over
使用send和__next__()区别:
1,send和next()都是让让生成器向下走一次
2,send可以给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器代码的时候不能使用send()
def func():
print('我吃什么啊?')
a = yield '馒头'
print(a)
b = yield '大饼'
print(b)
c = yield '韭菜盒子'
print(c)
print('程序结束')
yield 'Game Over'
gen = func()
ret1 = gen.__next__()
print(ret1)
ret2 = gen.send('胡辣汤') # send()会将"胡辣汤"发送给上一个yield的位置,赋值给a,进而打印a
print(ret2)
ret3 = gen.send('狗粮')
print(ret3)
ret4 = gen.send('猫粮')
print(ret4)

浙公网安备 33010602011771号