迭代器和生成器

一、迭代器
1、通俗理解

可以想象一下,你在计算机中做了一个加法,例:1+1=2 ,但是如果你在按 ‘= ’(等于符号),计算器会自动往下递增1,每按一次等于号,会+1个数字,那么就可以大概理解为,一个容器中存储了很多个东西,但是这个东西,按一定的规律规则来存储计算生成,但是容器中不可能什么东西都有吧,对的,人为的就会设一个限制,当超出这个限制范围,就会报异常

当然如果学过C语言的话,可以理解为这个空间存储了一个复杂的指针指向了很多地址,他实质不会占用很多内存,因为存储的是地址(这块可能理解的不到位,若有错,请指明)

迭代器最大的优点就是节省了空间,因为如果你想存储0~9这个字符串,你就需要劈开10个空间,但要是有了迭代器,那么你就开辟了一个空间

那么又会想到有了for 循环为什么不用for循环,而专门弄了一个抽象的迭代器呢,是因为,for循环就像是一个实例化的迭代器,已经具备了定义好的某种功能,而有了迭代器这个抽象的东西,你就可以按照自己的需求来定义合适的迭代器

2、实例

在Python中的列表、元组、字符串、文件、映射、集合等容器中都可以在for循环中使用,而迭代器是实现了迭代器协议方法的对象或类。在每次循环中,for语句都是从迭代器序列中取一个数据元素。迭代器协议方法主要有__iter__()和—__next__()方法,前者返回对象本身,后者返回容器中的下一个元素,当已经是最后一个元素时,就会引发StopIteration的异常。(int和bool类型不能用 )

看下面实例(迭代器的内部实现)就好理解了。

class iterInner(object):
....def __init__(self,end):
........self.__start = 0
........self.end = end
....def __iter__(self):
........return self
....def __next__(self):
........if self.__start >= self.end:
............raise StopIteration
........else:
............self.__start = self.__start + 1
............return self.__start - 1
ite = iterInner(8)
for i in ite:
....print(i)
'''
0
1
2
3
4
5
6
7'''
二、生成器
1、通俗理解

通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。如果想要100个包子,但是吃到50个时候吃饱了,但是已经做好了100个包子,那剩下的50个包子也吃不了,不就是浪费了吗?所以就产生了生成器,吃一个包子,产一个包子

  所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator

  生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。

2、简单说一下yield,send方法:

(1)yield方法:类似于return语句,调用到yield就会暂停,而且是一个yeield对应一个next,数量上要对应,但是yield不等于return语句,还是有一定的区别。

① return一般在函数中只设置一个,他的作用是终止函数,并且给函数的执行者返回值。

② yield在生成器函数中可设置多个,他并不会终止函数,next会获取对应yield生成的元素。

(2)send方法:直接引用其代码进行分析(最好自己调试几次便可领悟)

(第二个案例,小编又多调用了一次next,可以清楚对比出send和filed的区别)

# 1、 next只能获取yield生成的值,但是不能传递值。
def gen(name):
    print(f'{name} ready to eat')
    while 1:
        food = yield
        print(f'{name} start to eat {food}')
 
dog = gen('alex') # 由dog对象接受
next(dog) # 第一次传入,走gen函数,一直往下执行到yield停止,此时在等号右边
next(dog) # 第二次传入,直接接上一个走yield后面的内容,因为是一个无线循环,继续跳转到food=yield,但是由于上一执行的yield没有返回任何,所以返回的是一个None,依然遇到yield停止。
next(dog) #第三次传入,和第二个过程一样
 
# 执行结果如下:
# alex ready to eat
# alex start to eat None
# alex start to eat None
# 2、 而这个案例可以对比出来filed和send区别
def gen(name):
    print(f'{name} ready to eat')
    while 1:
        food = yield 222
        print(f'{name} start to eat {food}')
 
dog = gen('alex')
next(dog)  # 第一次必须用next让指针停留在第一个yield后面(因为是赋值运算先进行等号右边)
# 与next一样,可以获取到yield的值
next(dog) #第二次,传入时可得food为None,(因为上次运行完),第二次运行完依然为None
ret = dog.send('骨头') #第三次,传入时可得food为骨头,是因为send给上一个yield传递的值,但最后运行完,food值为222(因为从等号右边运行,直到完成,最后才赋值给左边)
print(ret) #打印结果为222
 
# 执行结果如下:
# alex ready to eat
# alex start to eat None
# alex start to eat 骨头
# 222
def gen(name):
    print(f'{name} ready to eat')
    while 1:
        food = yield
        print(f'{name} start to eat {food}')
 
dog = gen('alex')
next(dog) # 执行到这步游标在yield之后的位置,返回的是一个None值
 
dog.send('骨头') # 第一次,此时的光标从yield后面开始执行,进入时food变传递为骨头
dog.send('狗粮') # 第二次,进入时food变传递为狗粮
dog.send('香肠') # 第三次,进入时food变传递为香肠
由Debug调试运行了下过程,可以得到send方法实质就是给上一yield返回出的值,覆盖掉并传递现有的。

send注意!!!:yield和send同时在,不可在第一次就直接send,因为程序不知道你再搞什么,应该先执行一次next,让yield先走一遍,在去调用send,这样就可以把send传入的值返回给上一个yield,如果非要想这么做,可以send(None) 让第一次传入的是个None,(提示必须有个参,但又不能传入其他参数)

3、总结

  生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值