- 迭代:迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值
1 while True: 2 print('哈') #只是单纯的重复,因此不是迭代 3 4 lists = [1, 2, 3] 5 count = 0 6 while count < len(lists): 7 print(lists[count]) 8 count += 1
- 为何要有迭代器:对于序列类型的数据(列表,元组,字符串),我们可以使用索引的方法迭代取出每个元素,但对于非序列类型(集合,字典,文件类型等)这类没有的索引的数据,我们就无法使用上述方法,因此就要使用迭代器
- 迭代器协议:对象必须提供一个next方法,执行该方法的结束是要么返回迭代中的下一项,要么就引起一个Stoplteration异常以终止迭代(只能前进不能后退)
- 可迭代对象:内置有__iter__方法的对象,即obj.__iter__()
1 'hello'.__iter__() 2 [1, 2, 3].__iter__() 3 ('a', 'b', 1).__iter__() 4 {'name': 'chen', 'age': 18}.__iter__() 5 {'a', 1}.__iter__() 6 open('test').__iter__()
- 迭代器对象:可迭代对象执行obj.__iter__()得到的结果就是迭代器对象,而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象
1 # 文件类型是迭代器对象,因为其内部原始就有__next__()和__iter__()方法 2 open('a.txt').__iter__() 3 open('a.txt').__next__()
- 协议:一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for max min sum等函数)使用迭代器协议来访问对象
- 注:迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
for循环机制:
注:字符串、列表、元组、字典、集合、文件对象,因为其内部有__iter__()方法,故属于可迭代对象,但是本身没有__next__()方法,当用for遍历这些数据类型时,会进行下列操作:
1.调用这些数据类型中的__iter__()方法,返回值赋值给一个变量,那么这个变量就是迭代器对象
2.依据迭代器协议,调用__next__()方法,每调用一次就会返回其中的元素,当超出元素个数后,就会报StopIteration错,但是for循环会吞并这个错误,以此来终止循环。
1 #基于for循环,我们可以完全不再依赖索引去取值了 2 dic={'a':1,'b':2,'c':3} 3 for k in dic: 4 print(dic[k]) 5 6 #for循环的工作原理 7 #1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic 8 #2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码 9 #3: 重复过程2,直到捕捉到异常StopIteration,结束循环
用while循环模拟for循环机制:
1 l = [1, 2, 3] 2 iter = l.__iter__() 3 while True: 4 try: 5 print(iter.__next__()) 6 except StopIteration: 7 print('循环结束了哟!') 8 break
- 迭代器的优缺点
优点:----提供一种统一性,不依赖于索引的迭代方法
----惰性计算,节省内存
缺点:----无法获取长度(只有在next完毕后才知道到底有多少个值)
----一次性的,只能前进不能后退
- 生成器:是一种特殊的迭代器,自动实现迭代器协议
- 生成器优点:1.延迟计算(一次返回一个结果,即不会一次性产生所有结果,对于大量数据来说很有必要)
2.提高了代码的可读性
- 创造生成器的方法:
1.生成器表达式
1 generator = (i for i in range(5)) #生成器 2 print(generator) #<generator object <genexpr> at 0x000000C9B4BC0E08> 3 print(generator.__next__()) 4 print(generator.__next__()) 5 print(generator.__next__()) 6 print(generator.__next__()) 7 print(generator.__next__()) 8 print(generator.__next__()) #StopIteration
2.含有yield的函数:
- 语法上与普通函数基本相同,不同点在于用yield代替return来返回值,并且可以有多个yield
- 生成器每次调用__next__()方法时,都是从上次迭代结束的位置执行到下个yield的位置,然后将结束的位置作为下次__next__()迭代时的开始位置
1 def generator(): 2 yield '我' 3 print('开始生儿子啦!') 4 yield '儿子' 5 print('开始生孙子啦!') 6 yield '孙子' 7 8 generator_obj = generator() 9 # print(generator_obj) #<generator object generator at 0x000000F704810E08> 10 print('第一次', generator_obj.__next__()) #执行函数内容直到第一个yield后的'我'为止,并停止到这个为止,作为下次next调用的开始 11 #第一次 我 12 print('第二次', generator_obj.__next__()) #从print('开始生儿子啦!')执行到第二个yield后的'儿子'为止,作为下次next调用的开始 13 # 开始生儿子啦! 14 # 第二次 儿子 15 print('第三次', generator_obj.__next__()) #从print('开始生孙子啦!')执行到第三个yield后的'孙子'为为止。 16 # 开始生孙子啦! 17 # 第三次 孙子
生成器函数的优点:可以保留函数的运行状态,运行一次next后就可以进行别的操作,不必等到所有的数据都取出来后才进行操作,相当于“边做边卖”,而不是“都做完了才开始卖”’
母鸡下蛋的例子比较几种数据产生的方式:
1 #母鸡下蛋的几种实现方式 2 3 #列表解析的方式 4 egg_list = ['鸡蛋%s' %i for i in range(1, 5)] 5 print(egg_list) 6 #['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4'] 7 8 #普通函数:全部的蛋都下完了才能进行别的操作 9 #缺点:1.占用空间大 10 #2.效率低 11 def product_egg(): 12 ret = [] 13 for i in range(1, 5): 14 ret.append('鸡蛋%s' %i) 15 return ret 16 egg_list = product_egg() 17 print(egg_list) 18 #['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4'] 19 20 21 #生成器函数:边下边进行其他操作 22 #优点:1.尽可能减少内存的占用 23 # 2.效率高 24 def generator_egg(): 25 for i in range(1, 5): 26 yield '鸡蛋%s' %i 27 28 generator = generator_egg() 29 print(generator.__next__()) #鸡蛋1 30 print('休息') #休息 31 print(generator.__next__()) #鸡蛋2
来源:https://www.cnblogs.com/SakuraYuanYuan/p/10343077.html