Python-迭代器与生成器

迭代器

  • 什么是迭代?
    每次循环都是依赖上一个对象而来的

  • 什么是迭代器协议?
    一种约定,对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个stopieteration的异常,以终止迭代,只能向前走,不能往后。eg:for循环是遵循迭代器协议去访问对象

  • 什么是可迭代对象?
    实现了迭代器协议的对象,通过内部定义一个_iter_方法,可迭代对象必须提供一个next方法

对于for循环,惯例会想,for循环因为遵循迭代器协议去访问对象,认为for循环的对象肯定都是可迭代对象了,但是for循环可以迭代遍历字符串、列表、元祖、字典、集合、文件对象,他们因为本身没有next方法,所以它们不是可迭代对象,但是在for循环中,调用了这些类型内部的方法_iter_,把它们变成了可迭代对象,从而调用可迭代对象的next方法去取值,而for循环会捕捉stopiteration异常,以终止迭代。

#迭代器

x='hello'   #字符串本身不是可迭代对象
iter_test=x.__iter__()      #字符串类型内部有_iter_函数,调用这个iter函数就让其变成了可迭代对象
print(iter_test)   #iter_test就是一个可迭代对象了  #直接 iter()也可以的

#就会有_next_方法
print(iter_test.__next__())
print(iter_test.__next__())
print(iter_test.__next__())
print(iter_test.__next__())
print(iter_test.__next__())

结果:
<str_iterator object at 0x0000022ACFB932E8>
h
e
l
l
o
  • for循环的机制,就是遵循迭代器的协议,与索引机制没关系
#for循环的机制
l=[1,2,3,4]
for i in l:   # 先i_1=l._iter_()  再 i_1._next_() 
    print i
    
    #打印结束for循环可以捕捉到stopiteration异常,从而循环结束
  • 从而可以学到一种新的取列表的值得方法
l=[1,2,3,4]

print(l[0])  #法一,索引法

l_iter=l.__iter__()  #法二,先把列表l转化成一个迭代器
print(l_iter.__next__())
  
  结果:
  1
  1
  

for循环的作用:
字符串、列表、元组这些都是序列,可以用索引法遍历,但是对于字典、集合、文件这些非序列类型,就必须要用迭代器了,遍历他们就得用for循环了。因此for循环提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的_iter_将其转化成一个迭代器,再使用迭代器协议去实现循环访问。所以能被for循环遍历的对象也得有个前提,那就是该类型
有_iter_方法

for循环打印字典,返回的是key值

dic={'a':123,'b':2}
dic_iter=dic.__iter__()
print(dic_iter.__next__())
结果:   #之所以是key值是因为可迭代对象的next函数取key,与for 循环没啥关系
a
  • 使用迭代器的好处
    用的时候才给你

  • 用while 去模拟for循环干的事

#用while 去模拟for循环
ll=[1,22,3,5,6,4]
ll_iter=ll.__iter__()
while True:
    try:
        print(ll_iter.__next__())
    except StopIteration:
        print('迭代完毕了,循环终止')
        break

结果:
1
22
3
5
6
4
迭代完毕了,循环终止
        
lll=['yidai','erdai','sandai']  #全占
lll_iter=lll.__iter__()  #就只得到了一个地址,所以在其他地方用的话,用它传输更省存储
print(lll_iter.__next__())
print(next(lll_iter))   #与上者效果相同,但next是内置函数,其原理就是在调用 lll_iter._next_()函数,只不过next是python提供的,而 _next_()是 由数据类型提供的

迭代器=可迭代对象
要想把一个对象变成迭代器,就调用_iter_方法
什么是可迭代对象:必须实现可迭代协议
什么是可迭代协议:对象必须有_next_方法

生成器

  • 什么是生成器?
    就是一种数据类型,这种类型自动实现了迭代器协议,不用再调用_iter_方法了,所以生成器就是可迭代对象。

  • 在python中有两种表达方式
    1、生成器函数
    常规定义,但是用yied语句而不是return 返回结果,yied语句一次返回一个结果,在每个结果中挂起函数的状态,以使下次从它离开的地方继续执行。

def test():
    yield 1

g=test()
print(g)   #通过看打印结果,可知这就是个生成器
print(g.__next__())  #因为本身就是可迭代对象,所以就有_next_函数,打印出1
#print(g.__next__())  因为作用相当于return  只有一个值,所以再打印就报错stopiteration

结果:
<generator object test at 0x0000022ACFB79A98>
1
yield可以多次,多次打印就不再出错了
def test():
    yield 1
    yield 1
    yield 1

g=test()
print(g)   #通过看打印结果,可知这就是个生成器

print(g.__next__())  #因为本身就是可迭代对象,所以就有_next_函数
print(g.__next__())
print(g.__next__())

结果:
1
1
1

理解生成器保留执行状态:

#附:生成器函数

def test2():
    print('letty')
    yield '我'
    print('no')
    yield '123'
    
    
#理解生成器(迭代器)的保留状态是什么意思
res=test2()
print(res.__next__())
第一次打印的结果:
letty
我

第二次打印的结果:从上次执行完的地方开始执行
no
123

生成器函数的好处:
可以再两次_next_中间增加代码
保留上次的执行状态

2、生成器表达式
ps:三元表达式

name='letty'
res='girl' if name=='letty' else 'unknow'
print(res)

结果:
girl

类似于列表推导,但是生成器返回按需产生结果的

ps:列表解析

#列表解析:

egg_list=[]
for i in range(10):
    egg_list.append('鸡蛋%s' %i)

print(egg_list)

结果:
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']

如果进行列表解析:,达到相同的效果
直接生成一个列表,并且放到了内存
有个缺点,如果数量很大,会很占内存
l=['鸡蛋%s' %i for i in range(10)]
print(l)

结果:
['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']




 #再加入三元表达式,没有四元表达式
ll=['鸡蛋%s' %i for i in range(10)  if i>5]
print(ll)

结果:
['鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
lao=('鸡蛋%s' %i for i in range(10))  #生成器表达式,其结果几乎不占内存
print(lao)
print(lao.__next__())
print(lao.__next__())
print(lao.__next__())
print(lao.__next__())
print(next(lao))
结果:
产生一个生成器:
  <generator object <genexpr> at 0x00000220F8EEC990>
鸡蛋0
鸡蛋1
鸡蛋2
鸡蛋3
鸡蛋4


summary:
列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存,生成器要一个给一个
Python不但使用迭代器协议,让for循环更加通用,大部分内置函数,也是使用迭代器协议访问对象。eg sum、map、reduce、filter等等
[i for i in range(10)] #这会直接生成一个列表,如果数据非常大就非常占内存,可以[]变成()就会产生一个生成器,进行传输的时候就非常节省内存了。

特性总结:
1)语法上和函数类似:差别在于yield返回值
2)自动实现迭代器协议:
3)状态挂起
使用生成器的优点:
1)延迟计算,一次返回一个结果,不会一次生成所有结果,这对于大多数的数据处理会非常有用
2)有效提高了代码可读性
代码行数更少,在保证代码可读性的情况下,代码行数越少越好
3)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值