实用的迭代器: 生成器
一,生成器初识
-
生成器本质上就是迭代器
-
两者唯一的区别:
-
迭代器都是python提供的或者通过数据转换的来的
-
生成器需要我们自己用python代码构建
-
通过生成器函数: yield
def func(): print(11) return 22 ret = func() print(ret) # 22 # 将函数中的return换成yield,这样func就不再是函数了,而是一个生成器函数 def func(): print(11) yield 22 gen = func() # 不会执行函数,而是获取这个生成器对象(就是那个内存地址). ret = next(gen) # 这个时候函数才会执行 print(ret) # 并且yield会将func生产出来的数据 22 返回给 ret
# 并且生成器函数中可以写多个yield # yield可以看做暂停,而next可以看做开始 def func(): yield 22 yield 33 yield 44 gen = func() ret = next(gen) print(ret) # 22 ret2 = next(gen) print(ret2) # 33 # 和迭代器一样,next超过yield的个数就会报错,StopIteration
-
yield和return的区别
-
return一般在函数中只设置一个,他的作用是终止函数,并且给函数的执行者返回值,多个值通过元组的形式返回
-
yield在生成器函数中可设置多个,他不会终止函数,next会获取对应yield生成的元素,多个值也是通过元组的形式返回
def eat(): lst = [] for i in range(1, 2001): lst.append('包子'+str(i)) return lst e = eat print(e) # 一次性全加载出来,占内存 def eat(): for i in range(1, 2001): yield '包子'+str(i) e = eat() for i in range(200): # 运行200次next print(next(e)) # 运行一个next就会产生一个包子,非常节省内存,而且可以保留上次的位置
-
-
send()
# next只能获取yield生成的值,但是不能传递值 # send可以给上一个yield发送值,但是第一次必须用next让指针停留在第一个yield后面. def gen(name): print(f'{name}准备开吃了!') while 1: food = yield print(f'{name}准备开吃{food}了!') haha = gen('马倩') next(haha) # 第一次使用next让指针停留在第一个yield后面 haha.send('骨头') # send给上一个yield发送值 # 会打印 马倩准备开吃骨头了
- 相同点:send和next()都可以让生成器对应的yield向下执行一次,都可以获取到yield生成的值
- 不同点:send可以给上一个yield传递值
-
yield from
-
python3中提供一种可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回的方法
# 对比yield和yield from def func(): lst = ['卫龙', '老冰棍', '北冰洋', '巧克力'] yield lst gen = func() print(next(gen)) # 只是返回一个列表 def func1(): lst = ['卫龙', '老冰棍', '北冰洋', '巧克力'] yield from lst g = func1() print(g) # 它会将这个可迭代对象(列表)的每个元素当成迭代器的每个结果进行返回 print(next(g)) print(next(g)) print(next(g)) print(next(g)) ''' yield form ['卫龙', '老冰棍', '北冰洋', '巧克力'] 等同于: yield '卫龙' yield '老冰棍' yield '北冰洋' yield '巧克力' '''
-
-
因为yield from 是将列表中的每一个元素返回,所以如果写两个yield from 并不会产生交替的效果
-
意义: yield from 节省代码,提升效率(代替了for循环)
-
-
二,列表推导式与生成器表达式
-
列表推导式:用一行代码构建一个有规律的列表
lst = [] for el in range(10): lst.append(el) print(lst) # [0,1,2,3,4,5,6,7,8,9] lst1 = [el for el in range(10)] # 列表推导式 print(lst1) # [0,1,2,3,4,5,6,7,8,9] # 循环模式 lst2 = [el for el in range(10) if el % 2 == 1] print(lst2) # [1,3,5,7,9] 筛选模式 # 1.循环模式: [变量(加工的变量) for 变量 in iterable(可迭代对象)] # 2.筛选模式: [变量(加工的变量) for 变量 in iterable if 条件]--就是在循环模式的基础上加上一个判断条件,将满足条件的变量留到列表中
-
生成器表达式
-
生成器表达式和列表推导式的语法上一模一样,只是把[]换成()就行了
gen = (el**2 for el in range(1,11)) print(gen) # 生成器内存地址
-
如何触发生成器或迭代器
- next
- for循环
- 转换,比如用list()转换成列表
-
生成器的惰性机制
生成器只有在访问的时候才取值,说白了.你找他要才给你值.不找他要.他是不会执行的.
-
-
生成器表达式和列表推导式的区别
- 列表推导式比较耗内存,所有数据一次性加载到内存,而生成器表达式遵循迭代器协议,逐个产生元素
- 得到的值不一样,列表推导式得到的是一个列表,生成器表达式获取的是一个生成器
- 列表推导式一目了然,生成器表达式只是一个内存地址
-
字典推导式
lst1 = ['jay', 'jj', 'meet'] lst2 = ['周杰伦', '林俊杰', '马倩'] dic = [lst1[el]: lst2[el] for el in range(len(lst1))] print(dic) # {'jay': '周杰伦', 'jj': '林俊杰', 'meet': '马倩'}
-
集合推导式
s = {i for i in lst} print(s) # {1, 2, 3, 9, -7, -3, -1}