主要内容
- 生成器
- 生成器函数
- 列表推导式
一 什么是生成器: 本质就是迭代器
在python中有三种方式来获取生成器:
1 通过生成器函数
2通过各种推导式来实现生成器
3通过数据的转换也可以以(获取)生成器
例子1 def func(): print(111) return 22 a=func() # 111 print(a) #222 #
二 生成器函数
retuurn和yield的关系:
return是用来返回具体的某个值,yield一般与循环一起用,相当于生成了一个容器(常见的就是字典),
然后在这个容器里面存放了每次循环以后的值,并且就在那放着,不输出,不返回,
等你下次需要他的时候直接取出来用(调用)就行
yield和__next__()的关系:
yield关键字的特点: 可以记录当前函数中执行的位置,下一次继续执行
next和yield是一对搭档 : next开始函数的执行 yield停止函数的执行
例子2
def func(): print(111) yield 222 ret=func() print(type(ret)) #<class 'generator'> generator:生成器 print(ret) # <generator object func at 0x00000148B4751D58>
运行结果和上面不一样 由于函数中存在了yield,那么此时的函数就是一个生成器函数
这个时候 再用函数的调用方法就不行了,此时该函数不执行,而是获得一个生成器
想要执行此函数就可以直接__next__()来执行,因为生成器的本质就是迭代器.
def func(): print(111) yield 222# 函数遇到yiled会暂时停止和retun的区别在于 yiled是分段执行函数 return是直接停止函数 ret=func() # 函数此时不执行,而是返回一个生成器 (内部有__iter__()方法),执行就跟迭代器一样了 print(type(ret)) 查看是什么类型 结果为 generator print(dir(ret)) # 查看内部方法 a=ret.__next__() # 执行方式:迭代器 点__next__() 可以赋值 遇到这才开始进入函数 自上而下运行
不过如果你是一个yield就暂停到这 print(a) print(ret.__next__()) # 此时函数报错,是因为只有一个yiled 可以理解为一个__next__()执行一个yield, __next__() 本来就是一个一个拿 而恰巧遇到yield 暂停函数(分段执行)
生成器的一个简单作用: (例子:订衣服)
一般函数解决:
def cloth(): lst=[] for i in range(0,10000): lst.append('衣服'+str(i)) return lst a=cloth() print(a) # 一次全拿出来 放到列表中
不完美 目的是我想要几个你给我几个(这样写太占内存,不好)
所以:
def cloth1(): for i in range(0,10000): yield '衣服'+str(i) # 惰性机制 内部自己执行自加一,不会全部加载出来 g=cloth1() # 返回一个生成器 print(cloth1().__next__()) # 迭代器 点 __next__() 执行 for i in range(10): # 取十件 print(g.__next__()) # ⽣成器是⼀个⼀个的指向下⼀个. 不会回去, __next__()到哪, 指针就指到哪⼉. 下⼀次继续获取指针指向的值. print(g.__next__()) # 从第十个数开始 我又取了一件 for i in g: # g他本身也是一个生成器 ,内部含有__iter__() 所以可迭代 内部__next__()不冲突 print(i)
send 方法 和__next__()一样 都可以让生成器执行到下一个yield
def func(): print('今天吃的什么饭') a=yield '馒头' print(a) yield '狗粮' a=func() ret=a.__next__() print(ret) ret1=a.send('胡辣汤') # 相当于 __next__() print(ret1)
send和__next__()都是让程序向下走一次
send可以让yield的位置传递参数,不能给最后一个yield发送值 会报错,第一次执行函数是不能用send,就是先用__next__()开下头
for循环可以直接循环生成器来获取内部元素
def func(): print(111) yield 222 print(333) yield 444 print(555) yield 666 gen = func() for i in gen: print(i) 结果: 111 222 333 444 555 666
三 列表推导式,⽣成器表达式以及其他推导式
列表推导式是通过一行来构建你想要的列表,常用写法,[结果 for 变量 in可迭代对象]
lst = [i for i in range(1, 15)] print(lst)
# 还可以进行筛选:
# 获取1-100所有的偶数
print([i for i in range(1,101) if i%2==0])
生成器表达式 和 列表表达式一样,只是把[]替换成()
例子: ss=(i for i in range(10)) print(ss) #generator 直接打印出来就是一个生成器
我们可以用for循环来循环这个生成器: sw=('第%s次打你'% i for i in range(10)) for i in range(2): s=sw.__next__() print(s) # 输出0和1 两次 因为取了两个数 for i in sw: print(i) # 全部打印出来,for循环内部有__next__()的东西,还会帮你处理异常(报错)
生成器表达式也可以进行筛选
例子: ss=(i*i for i in range(100) if i%3==0) for i in ss: print(i) # 打印出100以内所有能被3整除的数的平方
例子: 寻找名字中带有两个e得人的名字 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] # 列表推导式写法: gen = (name for first in names for name in first if name.count("e") >= 2) for name in gen: print(name)
正常代码写法:
lst=[]
for name in names:
for na in name:
if na.count('e')>=2:
lst.append(na)
print(lst)
小结:
⽣成器表达式和列表推导式的区别:
1. 列表推导式比较耗内存. ⼀次性加载. ⽣成器表达式⼏乎不占⽤内存. 使⽤的时候才分
配和使⽤内存
2. 得到的值不⼀样. 列表推导式得到的是⼀个列表. ⽣成器表达式获取的是⼀个⽣成器.
举个栗⼦.
同样⼀篮⼦鸡蛋. 列表推导式: 直接拿到⼀篮⼦鸡蛋. ⽣成器表达式: 拿到⼀个老⺟鸡. 需要
鸡蛋就给你下鸡蛋.
⽣成器的惰性机制: ⽣成器只有在访问的时候才取值. 说⽩了. 你找他要他才给你值. 不找他
要. 他是不会执⾏的.
补充:
def func(): print(111) yield 222 g=func() # 生成器 g1=(i for i in g) # print(g1.__next__()) print(list(g)) # 通过数据转换可以获取生成器相当于print (g.__next__())
执行顺序 当你取值的时候函数才会被执行, 而且你只有一个__next__()取一次少一个
总结:
推导式有, 列表推导式, 字典推导式, 集合推导式, 没有元组推导式
⽣成器表达式: (结果 for 变量 in 可迭代对象 if 条件筛选)
⽣成器表达式可以直接获取到⽣成器对象. ⽣成器对象可以直接进⾏for循环. ⽣成器具有
惰性机制.
目前知道了 获取生成器的几种方式:
直接生成器点__next__()
for 循环(目的也是加了__next__())
通过数据转换可以,不过他会把取得值放到你所转的类型中去