迭代器,生成器
一.认识迭代器:
迭代器:
器:工具
迭代:重复(更新换代)的过程,这种过程每次迭代都是基于上一次的结果
迭代器:迭代取值的工具
为什么用迭代器:
迭代提供了一种不依赖于索引取值的方法
优点;
1.可以不依赖索引取值
2.值占用一份内存空间,不会内存溢出 (python3与python2区别)
缺点:
1.不能去指定的元素
2.取完值会报错 StopIteration
n = 0 while True: print(n) 不算迭代器 没有迭代 n = 0 while True: print(n) n += 1 是迭代器 有迭代 + 重复
二.可迭代对象与迭代器对象
可迭代对象:
内置有__iter__方法的都是可迭代对象 . __iter__ 也可以写成 iter()
str,list,tuple,dict,set,file
迭代器对象:
内置有__iter__方法,__next__方法是迭代器对象. __next__ 迭代器取值
file
可迭代对象使用__iter__方法就得到该对象的迭代器对象
迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象
可迭代对象转迭代器对象 __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 + except StopIteration 截获到住取完时的报错信息,结束循环,就不会报错
三.for循环的本质
for循环本质:
1.现将 in 后面的可迭代对象调用__iter__方法转成迭代器对象
2.再调用__next__方法 迭代取值
3.内部有异常捕获,当值取完,出现 StopIteration ,自动结束循环
迭代取值的本质就是for循环
问: 文件本身就是迭代器对象,为什么还会有__iter__方法? 因为迭代取值的本质是for循环,for循环第一步是将in后面的可迭代对象调用__iter__方法转成迭代器对象, 文件也是通过for循环取值,要经理for循环取值的第一步,所以文件有__iter__方法,但不会改变文件
l = [1,2,3] for i in l: print(i) res = l.__iter__() print(res.__next__()) print(res.__next__()) print(res.__next__())
五.生成器:
生成器:
用户自定义的迭代器
表达式:
res =(值 for i in 可迭代对象 if 条件) (*****)
生成器不会主动执行任何一行代码,必须通过__next__触发代码执行
函数版生成器:
def func(参数...):
...
yield 值
...
g = func()
关于yield:
1.函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行,会将函数初始化成迭代器
2.yield后面跟的值就是调用迭代器__next__方法你能得到的值
3.yield会将函数暂停住
4.yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回
5.yield支持外界为其传参(了解)
与return之间异同点
相同点:都可以返回值,并且都可以返回多个
不同点:
yield可以返回多次值,而return只能返回一次函数立即结束
yield还可以接受外部传入的值
生成器表达式:
例1:
res = (i for i in range(1,10) if i != 4) print(res) # >>> <generator object <genexpr> at 0x000002272C5B0780> print(res.__next__()) print(res.__next__()) print(res.__next__()) print(res.__next__()) 例2: #占内存 f = open('xxx.txt','r',encoding='utf-8') data = f.read() print(len(data)) f.close() with open('xxx.txt','r',encoding='utf-8') as f: # n = 0 # for line in f: # n += len(line) # print(n) g = (len(line) for line in f) # print(g.__next__()) # print(g.__next__()) # print(g.__next__()) # print(g.__next__()) print(sum(g))
函数版生成器:
def func(): print('--第一次--') yield 1 print('--第二次--') yield 2 print('--第三次--') yield 3 print('--第四次--') yield res = func() # 初始化成迭代器 print(res) print(res.__next__()) print(res.__next__()) print(res.__next__()) print(res.__next__()) ''' <generator object func at 0x000001CDAAB0A938> --第一次-- 1 --第二次-- 2 --第三次-- 3 --第四次-- None '''
range()原理
for i in range(1,8,2): print(i) # >>> 1 3 5 7
def my_range(x,y,z=1): while x < y: yield x x += z res = my_range(1,4) # 把my_range 初始化成迭代器 print(res) # >>> <generator object my_range at 0x00000297384B0780> print(res.__next__()) # >>> 1 print(res.__next__()) # >>> 2 print(res.__next__()) # >>> 3 for i in my_range(1,8,2): print(i,end=' ') # >>> 1 3 5 7
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 吃了 饺子 '''
题:
def add(n,i): return n+i def test(): for i in range(4): yield i g=test() # 函数test初始化成迭代器 (不运行) for n in [1,10]: g=(add(n,i) for i in g) # 第一次for循环g=(add(n,i) for i in test()) 此时n = 1 # 第二次for循环g=(add(n,i) for i in (add(n,i) for i in test())) 此时 n = 10 # print(n) res=list(g) """ 1.list是基于for循环,for循环内有__next__ 触发 g 的内部代码运行 2.g 中的 (add(n,i) for i in test()) 先运行变成 (10+0 ,10+1,10+2,10+3) 3.此时变成 g = (add(n,i) for i in (10,11,12,13) 最终是 10+10,10+21,10+22,10+23 """ #A. res=[10,11,12,13] #B. res=[11,12,13,14] #C. res=[20,21,22,23] 答案 #D. res=[21,22,23,24]