一、什么是迭代器
首先回顾一下,什么是递归?
在调用一个函数的过程中,直接或间接地调用了函数本身这个就叫递归。
举个问路的例子,你问张三火炉山在哪里?张三不知道,他说我帮你去问李四,李四对张三说我也不知道,你等着,我帮你去问王五。于是李四就去问王五,王五告诉李四 火炉山在xxx,李四就屁颠屁颠地跑回去告诉张三,张三知道答案后就告诉你,前面就是火炉山!
什么是迭代?
迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
举个造人的例子,你爷爷跟你奶奶生了你爸爸,你爸爸和你妈妈生了你,然后一代代这样繁衍下去....如果没有你爷爷奶奶,也就没有你爸爸,没有你爸爸也就没有你
什么是迭代器?
可以被next()函数调用并不断返回下一个值的对象称为迭代器,即迭代的工具
什么是可迭代对象?
实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)。一般把可以直接作用于for循环的对象统称为可迭代对象(Iterable),python内部的工具,如for循 环,sum,min,max,socket等都使用迭代器协议访问对象
x=‘hello‘
print(dir(x))
iter_test=x.__iter__()
print(iter_test)
print(iter_test.__next__())
print(iter_test.__next__())
print(iter_test.__next__())
print(iter_test.__next__())
print(iter_test.__next__())
print(iter_test.__next__())
什么是迭代器协议?
对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代(只能往后走不能往前退)
字符串、列表、元组等为什么是可迭代对象?
因为它们是可以被遍历的数据类型,也就是元素可以被一个一个取出来。
s={1,2,3}
for i in s: #只要能被for循环遍历的对象,都有__iter__()方法
print(i)
iter_s=s.__iter__()
print(iter_s)
print(iter_s.__next__())
print(iter_s.__next__())
print(iter_s.__next__())
print(iter_s.__next__())
迭代器与可迭代对象的区别?
区别就是在迭代器里面有__next__方法,这也是它们之间的最根本区别。
l=[1,2,3]
iter_l=l.__iter__() #遵循迭代器协议,l是可迭代对象,iter_l是迭代器
print(iter_l.__next__())
print(iter_l.__next__())
为什么要有for循环?
因为字典、集合、文件对象这种数据类型是没有下标的,并且是无序的。若想取出其内部包含的元素,则必须找出一种不依赖于索引的方式,所以就需要有for循环。
l=[1,2,3]
index=0
while index < len(l): # 不用下标索引遍历列表中的元素
print(l[index])
index+=1
dic={‘a‘:1,‘b‘:2} #for循环为什么遍历的是字典的key?全是因为迭代协议中的next方法返回的值
iter_d=dic.__iter__()
print(iter_d.__next__())
f=open(‘test.txt‘,‘r+‘)
iter_f=f.__iter__()
print(iter_f)
print(iter_f.__next__(),end=‘‘)
print(iter_f.__next__(),end=‘‘)
for循环的工作原理
字符串、列表、元组、字典、集合、文件对象这些都是可迭代对象,for循环在遍历他们的时候,其实首先调用了它们内部的iter方法,
把他们变成一个可迭代对象,然后再调用可迭代对象的next方法取值,捕捉到StopIteration异常,就终止迭代。
l=[1,2,3,4,5]
diedai_l=l.__iter__()
while True: #模拟for循环
try:
print(diedai_l.__next__())
except StopIteration:
print(‘迭代完毕了,循环终止了‘)
break
迭代器的优缺点
#优点:
- 提供一种统一的、不依赖于索引的迭代方式
- 惰性计算,节省内存
#缺点:
- 无法获取长度(只有在next完毕才知道到底有几个值)
-一次性的,只能往后走,不能往前退
二、生成器
什么是生成器?
只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码。可以理解为一种数据类型,这种数据类型自动实现了迭代器协议,所 以生成器就是可迭代对象。
def test():
yield 1 #帮你返回值,并把你变成生成器
yield 2
yield 3
g=test()
print(‘来自函数‘,g) #<generator object func at 0x0000000002184360>
print(g.__next__())
print(g.__next__())
表现形式
函数式
函数内部包含有yield关键字,使用yield语句返回结果,而不是return,yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执 行
def test():
yield 1 #帮你返回值,并把你变成生成器
g=test()
print(‘来自函数‘,g) #<generator object func at 0x0000000002184360>
表达式
首先认识一下三元表达式:
name=‘智障‘
name=‘小鲜肉‘
res=‘SB‘ if name == ‘智障‘ else ‘帅哥‘
print(res) #返回SB
所谓三元就是if name == ‘智障‘、else ‘帅哥‘、‘SB‘
再来认识一下列表解释:
egg_list=[]
for i in range(10):
egg_list.append(‘鸡蛋%s‘ %i)
print(egg_list)
l=[‘鸡蛋%s‘ %i for i in range(10)]
l1=[‘鸡蛋%s‘ %i for i in range(10) if i > 5 ] #可以,三元
# l1=[‘鸡蛋%s‘ %i for i in range(10) if i > 5 else i] #没有四元表达式
l2=[‘鸡蛋%s‘ %i for i in range(10) if i < 5]
l3=[‘鸡蛋%s‘ %i for i in range(10) else ‘haha‘] #报错,没有else
print(l)
print(l1)
print(l2)
最后就是生成器表达式:
类似于列表解释,但是生成器表达式返回一个按需产生结果的一个对象,而不是一次构建一个结果列表,比列表解释节省内存
laomuji=(‘鸡蛋%s‘ %i for i in range(10)) #生成器表达式
print(laomuji)
print(laomuji.__next__())
print(laomuji.__next__())
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
yield
1、把函数做成迭代器
2、对比return,可以返回多次值,可以挂起/保存函数的运行状态
生成器的好处
使用生成器会产生延迟操作,所谓延迟操作是指在需要的时候才产生结果,而不是立即产生结果,这也是生成器的主要好处