生成器 推导式
生成器 Generator
本质:(就是迭代器)
- 迭代器(所以自带了(_ iter ) 方法和 ( next _)方法,不需要我们去实现)
特点:
- 惰性运算,开发者自定义
生成器的构建方式
生成器函数
ef func():
print(11)
yield 22
ret = func()
print(ret)
# 运行结果:<generator object func at 0x000001A575163888> 生成器
print(next(ret)) # 22 一个next对应一个yield
生成器推导式(表达式)
python内置函数或者模块提供
def func():
print("111")
yield 222
print("333")
yield 444
gener = func() # 这个时候函数不会执⾏. ⽽是获取到⽣成器
ret = gener.__next__() # 这个时候函数才会执⾏
print(ret) # 并且yield会将func生产出来的数据 222 给了 ret。
ret2 = gener.__next__()
print(ret2) # 最后⼀个yield执⾏完毕. 再次__next__()程序报错,与迭代器一样。
# 打印结果: 111
222
333
444
return 与 yield的区别
return
- 一般在函数中只设置一个,他的作用是终止函数,并且给函数的执行者返回值。
yield
- 在生成器函数中可设置多个,他并不会终止函数,next会获取对应yield生成的元素。
- 一个yield有多个值以 元组 返回
生成器的作用:节省内存
send 方法(了解)
send注意事项
- 第一次使用生成器的时候 是用next获取下一个值
- 最后一个yield不能接受外部的值
# next只能获取yield生成的值,但是不能传递值。
def gen(name):
print(f'{name} ready to eat')
while 1:
food = yield
print(f'{name} start to eat {food}')
dog = gen('alex')
next(dog)
next(dog)
next(dog)
# 而使用send这个方法是可以的。
def gen(name):
print(f'{name} ready to eat')
while 1:
food = yield 222
print(f'{name} start to eat {food}')
dog = gen('alex')
next(dog) # 第一次必须用next让指针停留在第一个yield后面
# 与next一样,可以获取到yield的值
ret = dog.send('骨头')
print(ret)
def gen(name):
print(f'{name} ready to eat')
while 1:
food = yield
print(f'{name} start to eat {food}')
dog = gen('alex')
next(dog)
# 还可以给上一个yield发送值
dog.send('骨头')
dog.send('狗粮')
dog.send('香肠')
yield from
# 对比yield 与 yield from
def func():
lst = ['卫龙','老冰棍','北冰洋','牛羊配']
yield lst
g = func()
print(g)
print(next(g)) # 只是返回一个列表
def func():
lst = ['卫龙','老冰棍','北冰洋','牛羊配']
yield from lst
g = func()
print(g)
# 他会将这个可迭代对象(列表)的每个元素当成迭代器的每个结果进行返回。
print(next(g))
print(next(g))
print(next(g))
print(next(g))
'''
yield from ['卫龙','老冰棍','北冰洋','牛羊配']
等同于:
yield '卫龙'
yield '老冰棍'
yield '北冰洋'
yield '牛羊配'
'''
- yield from 是将列表中的每一个元素返回,所以 如果写两个yield from 并不会产生交替的效果
def func():
lst1 = ['卫龙', '老冰棍', '北冰洋', '牛羊配']
lst2 = ['馒头', '花卷', '豆包', '大饼']
yield from lst1
yield from lst2
g = func()
for i in g:
print(i)
# 打印结果 :
'''
卫龙
老冰棍
北冰洋
牛羊配
馒头
花卷
豆包
大饼
'''
yield 与 yield from 对比
- yield : 对应next 给 next 一 一 返回值
- yield from : 将迭代对象的每一个元素返回给 next
yield from 作用:
- 节省代码,提升效率(代替了for循环)
推导式
列表推导式
优点 :
- 简单 快捷
缺点 :
- 可读性不高 不好排错
li = []
for i in range(1,101):
li.append(i)
print(li)
li = [i for i in range(1,101)] #列表推导式(循环模式)
print(li)
构建方式
循环模式:[变量(加工的变量) for 变量 in iterable]
筛选模式 : [变量(加工的变量) for 变量 in iterable if 条件]
# 要求做一个列表 1-100
li = [i for i in range(1,101)]
print(li)
# 要求做一个1-10之内的平方和的列表
li = [i*i for i in range(1,11)]
print(li)
# 1-100内 所有的偶数
li = [i for i in range(1,101) if i % 2 == 0]
print(li) # 这个就是筛选模式(有条件判断)
li = [i for i in range(2,101,2)]
print(li)
# "python1期"到"python100期"的列表
li = [f'python{i}期' for i in range(1,101)]
print(li) # f'python{i}',就是加工的变量
多层列表推导式
#找到嵌套列表中名字含有两个‘e’的所有名字(有难度)
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
li = []
for i in names:
for e in i:
if e.count("e") > 1:
li.append(e)
print(li)
print([e for i in names for e in i if e.count("e") == 2]) # 两层
生成器表达式
- 节省内存
- 与列表推导式几乎一模一样,只是把[]换成()就行了
循环模式
gen = (i**2 for i in range(10))
print(gen)
# 结果: <generator object <genexpr> at 0x0000026046CAEBF8>
筛选模式
# 获取1-100内能被3整除的数
gen = (i for i in range(1,100) if i % 3 == 0)
for num in gen:
print(num) # 打印结果就是for遍历了 所有在gen里的元素
print(next(gen)) # 要几个打印几个 一次只打印一个 记录位置
print(*gen) # 横向打印所有的gen里的元素
print(list(gen)) # 打印一个列表 (list可换成set / tuple / ) 字典除外
如何触发生成器(迭代器)取值
nxet
for / for i in li print(next(li))
print(list(obj)) 直接打印出一个列表 转化成容器类型(字典除外) 常用list转化
print(*obj)
字典推导式 循环模式 / 筛选模式
lst1 = ['jay','jj','meet']
lst2 = ['周杰伦','林俊杰','郭宝元']
dic = {lst1[i]:lst2[i] for i in range(len(lst1))} # 两个列表长度必须一样
print(dic)
集合推导式 循环模式 / 筛选模式
lst = [1,2,3,-1,-3,-7,9]
s = {abs(i) for i in lst}
print(s) # 天然去重
生成器表达式和列表推导式的区别:
生成器表达式
- 生成器表达式遵循迭代器协议,逐个产生元素
- 生成器表达式获取的是一个生成器
- 生成器表达式只是一个内存地址
列表推导式
- 列表推导式一目了然
- 比较耗内存,所有数据一次性加载到内存
- 得到的值不一样,列表推导式得到的是一个列表.生成器表达式获取的是一个生成器
无论是生成器表达式,还是列表推导式,他只是Python给你提供了一个相对简单的构造方式,因为使用推导式非常简单,所以大多数都会为之着迷,这个一定要深重,推导式只能构建相对复杂的并且有规律的对象,对于没有什么规律,而且嵌套层数比较多(for循环超过三层)这样就不建议大家用推导式构建。
生成器的惰性机制: 生成器只有在访问的时候才取值,说白了.你找他要才给你值.不找他要.他是不会执行的.