迭代器和生成器
今天主要内容:
1.迭代器
2.生成器
一.迭代器
可以使用dir()函数来检查类的内部定义的函数;
s = "adjlafk"
print(dir(s)) #可以打印对象中的方法和函数
print(dir(str)) #也可以打印类中声明的方法和函数
在打印结果中,寻找__iter__如果能找到,那么这个类的对象就是一个可迭代对象.
for循环的东西都有__iter__函数,包括range也有.如果对象中有__iter__函数.那么我们认为这个对像遵守了可迭代协议.就可以进行迭代.这里的__iter__是帮助我们获取到对象的迭代器.我们使用__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__()) #门
print(c.__next__()) #StopIteration (报错)
当程序遇到StopIteration将结束循环
for循环要求必须是可迭代对象
for循环的内部,使用的就是迭代器.最开始的时候就是获取迭代器.
二.生成器
生成器实质就是迭代器.
在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 0x10567ff68>#内存地址
运行的结果和上面不一样,由于函数中存在了yield,那么这个函数就是一个生成器函数.这个时候,我们在执行这个函数的时候.就不再是函数的执行了.而是获取这个生成器.生成器的本质就是迭代器,所以我们直接执行__next__()来执行一下生成器.
def func():
print("111")
yield 222
gener = func() #这个时候函数不会执行,而是获取到生成器
ret = gener.__next__() #这个时候函数才会执行,yield的作用和return一样,也是返回数据.
print(ret)
结果:111 222
拿到生成器之后,就相当于拿到了迭代器. __next__()的作用是执行到下一个yield.
yield和return的区别:
1.如果函数中包含了yield,这个函数是一个生成器函数;执行函数的时候是:生成器.
2.生成器执行__next__(),执行到下一个yield.
3.yield的作用和return基本相同,但是只负责返回,不会结束函数.
4.return是结束函数.
好了到此生成器说完了,但是生成器有什么作用呢?我们看这样一个需求:一个公司向厂家订购了10000套工作服.
def cloth():
lst = []
for i in range(10000):
lst.append("衣服"+str(i))
return lst
ret = cloth()
这样操作的话,厂家会把一万套衣服一次性给我;这样的话我一下用不完,而且多余的也没有地方放.如果能一套一套的给我就好了.
def cloth():
for i in range(10000):
yield "衣服"+str(i)
ret = cloth()
print(ret.__next__())
print(ret.__next__())
print(ret.__next__())....
或者:
for i in range(50):
print(ret.__next__())
区别:第一种是直接一次性全部拿出来,会很占用内存.第二种使用生成器,一次就一个,用多少生成多少.生成器是一个一个的指向下一个.不会回去,__next__()到哪,指针就指到哪.下一次继续获取指针指向的值.
生成器和迭代器最大的优点就是节省内存,如果函数的最后没有yield,则会报错.
send()和__next__()一样,也是执行到下一个yield.
def eat():
print("我吃什么啊")
a = yield "包子"
print(a)
b = yield "大饼"
print(b)
print("程序结束")
yield "GAME OVER"
gen = eat() #获取生成器
ret = gen.__next__()
print(ret)
ret1 = gen.send("胡辣汤")
print(ret1)
ret2 = gen.send("羊肉泡馍")
print(ret2)
注意:第一次调用必须使用__next__()
send和__next__()区别:
1.send和__next__()都是让生成器向下走一次
2.send可以改上一个yield的位置传递值,不能给最后一个yield发送值.在第一次执行生成器代码的时候不能使用send()只能使用__next__()
生成器的优点:节省内存.