迭代器
- 思维打开:如何从列表中取值
- for循环
- 索引
- 迭代器应用在可for循环的数据中,即拥有
__iter__
方法- 字符串
- 集合
- 列表
- 元组
- range()
- f = open()
- enumerate 枚举
- 字典
- 可迭代协议
-
只要内部含有
__iter__
方法都是可迭代的print(__iter__() in dir([]))
-
- 迭代器协议
- 内部含有
__next__和__iter__
方法都是迭代器
- 内部含有
- isinstance(数据, 类型) 判断数据是否是类型
- 迭代器协议和可迭代协议
- 只要能被for循环的都是可迭代的
- 可迭代的
.__iter__()
就是一个迭代器 - 迭代器中的
__next__()
方法就是一个一个的获取值
迭代器的优点
-
迭代器可以自定取元素
-
节省内存空间
#模仿for循环获取列表的元素 l = [1, 2, 3, 4, 5] iterator = l.__iter__() while 1: try: print(iterator.__next__()) except: break
实际例子
- 实际告诉你这是个迭代器
- 可迭代对象
- 直接给你内存地址
用for的情况
- 只有内部有
__iter__()
方法的数据才能用for循环
生成器 -一个迭代器
- 本质也是迭代器
- 生成器可以记住自己已经迭代到哪儿了
- 迭代器不能记住自己迭代到哪儿了
生成器函数
-
本质上就是我们自己写的函数
-
只要含有
yield
就是生成器函数 -
函数
yield
和return
不能共用 -
yield
只能在函数里用 -
生成器函数执行之后会产生一个生成器作为返回值
#普通函数 def func(): print(1) return 'a' #调用 ret = func() print(ret) #结果 1 a #生成器函数 def generator(): print(1) yield 'a' #调用 ret = generator() print(ret) #结果 <generator object generator at 0x000000000> #返回的是一个内存地址 #换种方式调用 ret = generator() print(ret.__next__()) #结果 1 a
-
yield
不会像return
一样结束函数 -
yield
也会返回一个值 -
每调用一个
__next__()
就会输出一个yield返回的值#定义一个生成器函数 def generator(): for i in range(100): yield i g1 = generator() g2 = generator() #循环打印生成器函数内容 while 1: try: print(g1.__next__()) print(g2.__next__()) except: break #结果是同步运行,所以相当于双线程
生成器函数进阶
-
yield
后不再有函数体了def wrapper(): print(1) yield 8 print(2) yield 8 print(2) #后方没有yield可以打印但是报错
-
__next__()
和send
效果可以一致- 获取下一个值的效果一致
-
但是也有不同,
send()
能够传值- 只不过获取下一个值的时候给上一个
yield
传一个值
- 只不过获取下一个值的时候给上一个
-
send
使用注意事项-
第一个值必须用
__next__()
调取 -
最后一个yield不能接收外部的值
#send()不传入参数 def wrapper(): print('=') yield '第一个' print('==') yield '第二个' #调用 g = wrapper() print(g.__next__()) print(g.send(None)) #结果 = 第一个 == 第二个 #send传入任意参数,如'hello' def wrapper(): print('=') countent = yield '第一个' #这里给yield赋值countent print('~~', countent) #这里添加一个打印信息 print('==') yield '第二个' #调用 g = wrapper() print(g.__next__()) #第一个值的调取必须用__next__() print(g.send(hello)) #结果 = 第一个 ~~ hello #这里结果出现了hello说明参数传进去了 == 第二个
-
练习应用
-
一个计算战绩平均值的应用
#生成器函数,用来计算战绩平均值 def averge(): count = 0 aver = 0 num = 0 add = 0 while 1: num = yield add += num count += 1 aver = add/count yield aver #持续输入的函数,用来输入每局成绩 def print_num(): g = averge() while 1: num = int(input('输入:')) g.__next__() print(g.send(num)) print_num()
-
将上面的作业改成装饰器形式
-
预激活装饰器
#装饰器作为持续输入的函数 #装饰器为生成器函数提供用户输入 def print_num(func): def inner(*args, **kwargs): g = func(*args, **kwargs) while 1: num = int(input('输入:')) g.__next__() print(g.send(num)) return g return inner #生成器函数 @print_num def averge(): count = 0 aver = 0 num = 0 add = 0 while 1: num = yield add += num count += 1 aver = add/count yield aver #调用 averge()
-
-
yield from
用法def generator(): a = '12345' b = 'abcde' yield from a yield from b #调用 g = generator() for i in g: print(i) #结果 1 2 3 4 5 a b c d e
生成器表达式
列表推导式
list = [i for i in rang(10)]
#得到的结果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
生成器表达式
g = (i for i in range(10)) #生成器
print(g)
#结果
<generator object <xxx> at 0x0000000> #一个内存地址
#利用for循环获取生成器里的元素
for i in g:
print(i)
#结果
0
1
2
3
4
5
6
7
8
9
- 生成器表达式和列表推倒式区别
- 返回值不一样
- 括号不一样
- 二者优缺点
- 列表推倒式代码量少,观看直观,但是占内存
- 生成器表达式几乎不占用内存但是使用起来代码量多,观看不直观
经典面试题
-
生成器是一个有指针的数据类型,因此循环一遍之后指针在数据之后,无法再继续取值
#定义一个生成器函数 def generator(): for i in range(6) yield i g = generator() #生成器 g1 = (i for i in g) g2 = (i for i in g1) print(list(g1)) print(list(g2)) #结果 [0, 1, 2, 3, 4, 5] []