迭代器
迭代器即用来迭代取值的工具,而迭代是重复反馈过程的活动,其目的通常是为了逼近所需的目标或结果,每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值,单纯的重复并不是迭代
while True:
msg = input('>>: ').strip()
print(msg)
# 仅仅是单纯的循环,没有迭代,不是迭代器
下述while循环才是一个迭代过程,不仅满足重复,而且以每次重新赋值后的index值作为下一次循环中新的索引进行取值,反复迭代,最终可以取尽列表中的值
goods=['mac','lenovo','acer','dell','sony']
index=0
while index < len(goods):
print(goods[index])
index+=1
为什么要有迭代器
对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器
可迭代对象
要想了解迭代器为何物,必须事先搞清楚一个很重要的概念:可迭代对象(Iterable)。
从语法形式上讲,内置有__iter__方法的对象都是可迭代对象,字符串、列表、元组、字典、集合、打开的文件都是可迭代对象:
'hello'.__iter__
(1,2,3).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
{'a','b'}.__iter__
open('a.txt').__iter__
迭代器对象
调用obj.__iter__()方法返回的结果就是一个迭代器对象(Iterator)。
迭代器对象是内置有__iter__和__next__方法的对象,打开的文件本身就是一个迭代器对象,执行迭代器对象.__iter__()方法得到的仍然是迭代器本身,而执行迭代器.__next__()方法就会计算出迭代器中的下一个值。
迭代器是Python提供的一种统一的、不依赖于索引的迭代取值方式,只要存在多个“值”,无论序列类型还是非序列类型都可以按照迭代器的方式取值
可迭代对象转迭代器对象 __iter__
s = 'hello'
res = s.__iter__()
print(res) # 得到的是迭代器对象的内存地址
# >>> <str_iterator object at 0x0000027734C7E908>
调用__next__迭代取值
l = [1, 2, 3]
res = l.__iter__()
print(res.__next__()) # >>> 1
print(res.__next__()) # >>> 2
print(res.__next__()) # >>> 3
print(res.__next__()) # >>> 值取完再取,报错 StopIteration
d = {'name': 'waller', 'age': 20, 'hobby': 'read'}
res = d.__iter__()
print(res.__next__()) # >>> name
print(res.__next__()) # >>> age
print(res.__next__()) # >>> hobby
print(res.__next__()) # >>> 值取完再取,报错 StopIteration
print(d.__iter__().__next__()) # >>> name
print(d.__iter__().__next__()) # >>> name
print(d.__iter__().__next__()) # >>> name
# 可迭代对象转成迭代器对象后取值,又重新把可迭代对象转成迭代器对象再取值,所以取的都是第一个值
# 文件本身就是迭代器对象,无论调用多少个__iter__ 都是一样的结果
with open('file.txt', 'r', encoding='utf-8') as f:
pass
f_iter = f.__iter__()
print(f_iter is f) # True 文件对象调用__iter__方法后的内存地址和调用前一样
f.__next__() # 迭代器对象用__next__取文件里每行的内容
异常处理
d = {'name': 'waller', 'age': 20, 'hobby': 'read'}
res = d.__iter__()
while True:
try:
print(res.__next__())
except StopIteration:
print('值已取完')
break
# 对迭代器对象循环取值,通过try + excep
迭代器总结
1.迭代就是重复的过程,每一次重复称为一次迭代,并且没次重复的结果是下一次重复的初始值
2.python必须要提供一种不依赖索引取值的迭代方式 -> 迭代器
-
优点:
1.迭代器对象在内存中只是一个内存地址,不占空间
2.迭代器同一时刻在内存中只有一个值 ->更节省内存
-
缺点:
1.只能往后取,并且是一次性的(索引取值更灵活)
2.无法统计迭代器的长度
for循环的原理
-
1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
-
2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
-
3: 重复过程2,直到捕捉到异常StopIteration,结束循环
#基于for循环,我们可以完全不再依赖索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:
print(dic[k])
生成器
只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码
def func():
print('====>first')
yield 1
print('====>second')
yield 2
print('====>third')
yield 3
print('====>end')
g=func()
print(g) # <generator object func at 0x0000000002184360>
生成器内置有__iter__和__next__方法,所以生成器本身就是一个迭代器
print(g.__iter__) # <method-wrapper '__iter__' of generator object at 0x000002354AD2DEB8>
print(g.__next__) # <method-wrapper '__next__' of generator object at 0x000002354AD2DEB8>
练习
自定义函数模拟range(1,7,2)
def my_range(start,stop,step=1):
while start < stop:
yield start
start+=step
#执行函数得到生成器,本质就是迭代器
obj=my_range(1,7,2) #1 3 5
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj)) #StopIteration
既然生成器对象属于迭代器,那么必然可以使用for循环迭代,如下:
#应用于for循环
for i in my_range(1,7,2):
print(i)
yield
yield为我们提供了一种自定义迭代器对象的方法,yield所在的函数加括号执行就得到迭代器。
yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值
def dog(name):
print('%s 准备开吃'%name)
while True:
food = yield
print('%s 吃了 %s'%(name,food))
g = dog('xx')
g.__next__() # 必须先将代码运行至yield 才能够为其传值
g.send('狗不理包子') # 给yield左边的变量传参 触发了__next__方法
g.send('饺子')
'''
xx 准备开吃
xx 吃了 狗不理包子
xx 吃了 饺子
'''
表达式形式的yield也可以用于返回多次值,即变量名=yield 值的形式,如下
def eater():
print('Ready to eat')
food_list=[]
while True:
food=yield food_list
food_list.append(food)
e=eater()
next(e)
# Ready to eat
# []
e.send('蒸羊羔')
# ['蒸羊羔']
e.send('蒸熊掌')
# ['蒸羊羔', '蒸熊掌']
e.send('蒸鹿尾儿')
# ['蒸羊羔', '蒸熊掌', '蒸鹿尾儿'
yield后面跟的值就是调用迭代器__next__方法你能得到的值
yield会将函数暂停住
yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回
yield支持外界为其传参(了解)
来源:CSDN
作者:i0208
链接:https://blog.csdn.net/Waller_/article/details/104061644