一.生成器和生成器函数
生成器的实质就是迭代器
在Python中有三种方式来获取生成器:
1.通过生成器函数 yield 就是生成器函数
2.通过生成器表达式创建生成器
3.通过数据转换也可以获取生成器
例如:
def func():
print("我是周杰伦")
yield "昆凌"
ret = func() =====>这里是通过func()函数来创建一个生成器 ====>如果光执行func() 打印出来的是空 没有东西,因为它本身是个生成器,但没有为它赋予值
print(ret) =======>打印出来的结果是生成器的内存地址
例如: .__next__() 是下一个的意思,向下执行
def func():
print("我是周杰伦")
yield "昆凌"
g = func()
print(g.__next__()) ======>打印出来的结果是"我是周杰伦" "昆凌"
例如:
def func():
print("我是周杰伦")
return "昆凌"
print(func()) ======>打印出来的结果是"我是周杰伦" "昆凌"
return 与 yield 的区别
1.return 直接返回结果,结束函数的调用
2.yield 返回结果,可以让函数分段执行
例如:
def func():
print("AA")
yield "aa"
print("BB")
yield "bb"
#print("CC") =====>如果没有这两行或者是没有最后一行 ,最后一个.__next__()仍会继续执行,但是没有东西,所以就会报错
#yield "cc"
g = func()
print(g.__next__()) ======> "AA" "aa"
print(g.__next__()) ======>"BB" "bb"
print(g.__next__()) ======>(如果存在print("CC")和yield "cc"这两行 执行出来CC和cc)
生成器有一个有点就是节省内存
例如: 运用生成器的好处就是可以节省内存,需要一件的时候就拿一件,但是最后的结果还是这100件
def func():
i = 1
while i < 101:
yield "衣服%s" % i
i = i + 1
g = gen()
print(g.__next__()) ===>衣服1 ,__next__() 到哪里,就拿到哪里
print(g.__next__()) ===>衣服2
例如:
def func():
lst = []
for i in range(1,101):
lst.append("衣服%s" % i)
return lst =========>这个执行出来的是将全部的衣服放进列表里全部打印出来 这样耗时又费空间内存
func()
例如:
def func():
yield 11
yield 22
yield 33
g = func() =====>拿到的是生成器,生成器的本质是迭代器,迭代器可以被迭代,生成器的本身就是迭代器,所以生成器可以直接执行for循环
for i in g: ======>for循环本质是执行的是 .__next__()
print(i) =====>打印出来的结果是11 22 33遍历循环
send 和 .__next__()的区别:
1.send() 和 .__next__() 都可以让生成器向下执行一次
2.send可以给上一个yield传一个值,但是不能给最后一个yield传值,并且在第一次执行生成器的时候不能使用send()
例如: send例子
def func():
print("AA")
a = yield "aa"
print(a)
print("BB")
yield "bb"
print(a)
print("CC")
yield "cc"
g = func()
print(g.__next__()) ====>"AA""aa"
print(g.send(1)) =====> 1 "BB" "bb"
print(g.__next__()) =====> "CC" "cc"
二.列表的推导式
语法:[最终结果 for 变量 in 可迭代对象 if 筛选]
列表推导式比较耗内存,得到一个列表
例如: (常规写法)
lst = []
for i in range(1,15):
lst.append("python%s" % i)
print(lst) ======>打印出来是["python1","python2","python3"......"python14"]
例如: 列表推导式写法
lst = ["python%s" % i for i in range(1,15)]
print(lst)
例如:打印1~100能被3整除的数 放在列表里
lst = [i for i in range(1,101) if i %3 == 0)]
例如:打印100以内能被3整除的数的平方
lst = [i**2 for i in range(1,101) if i %3 == 0]
例如:找出名字中带有两个e的人的名字
names = [['Tom', 'Billy', 'Jefferson' , 'Andrew' , 'Wesley' , 'Steven' ,'Joe'],['Alice', 'Jill' , 'Ana', 'Wendy', 'Jennifer', 'Sherry' , 'Eva']]
lst = [ii for i in names for ii in i if ii.count("e") == 2]
三.生成器表达式
生成器表达式和列表推导式的语法基本一样,只是把[]替换成()
语法:(结果 for 变量 in 可迭代对象 if 筛选)
生成器表达式几乎不占内存,获取的是一个生成器
例如:
gen = (i for i in range(10))
print(gen) =====>打印出来的是生成器 只有内存地址
例如:
gen = ("麻花藤我第%s次爱你" % i for i in range(10))
for i in gen:
print(i)
生成器表达式 和 列表推导式 的区别:
1.列表推导式比较耗内存,一次性加载, 生成器表达式几乎不占用内存,使用的时候才进行分配和使用内存
2.得到的值不一样,列表推导式得到的是一个列表, 生成器表达式得到的是一个生成器
四.生成器的惰性机制
生成器只有在访问的时候才取值, 不到最后是不会拿值的!!!!!!!!!!!!
例如:
def func():
print (111)
yield 222
g = func() #生成器g
g1 = (i for i in g) #生成器g1 但是g1的数据来源于g
g2 = (i for i in g1) #生成器g2 g2的数据来源于g1
print(list(g)) ======>111 [222] 获取g中的数据,这时func()才会被执行,打印111,获取到222 g执行完毕
print(list(g1)) ======> [] #获取g1的数据 g1的数据来源是g 但是g已经取完了 g1也就没有了数据
print(list(g2)) ======> [] #同样g2也没有数据
五.字典推导式
语法:{结果 for 变量 in 可迭代对象 if 筛选} ====>结果是k:v
例如:
dic = {"a":1,"b":2}
new_dic = {dic[key]:key for key in dic}
print(new_dic)
例如:
lst1 = ["AA","BB","CC"]
lst2 = ["aa","bb","cc"]
dic = {lst1[i] : lst2[i] for i in range(len(lst1))}
=======>{"AA":"aa","BB":"bb","CC":"cc"}
六.集合推导式 (自动去重)
集合推导式可以帮我们直接生成一个集合,集合的特点是无序,不重复,所以集合推导式自带去重功能
语法:{结果 for 变量 in 可迭代对象 if 筛选} ====>结果是k
例如:
lst = ["AA","BB","CC","DD","AA","BB"]
s = {i for i in lst}
print(s) ======>{"AA","BB","CC","DD"}
变态面试题
def add(a,b):
return a + b
def test():
for r_i in range(4):
yield r_i
g = test()
for n in [2,10]:
g = (add(n,i) for i in g)
print(list(g)) =====>打印出来的结果是[20,21,22,23]
for n in [2,10]: #n的值为2 10
g = (add(n,i) for i in g)
当程序循环第一次的时候 n = 2 但是循环没有结束,不执行
g = (add(n,i) for i in test())
当程序循环第二次的时候 n = 10 到print()的时候才执行
g = (add(n,i) for i in (add(n,i) for i in test()))
当时这个时候不执行,只有当程序执行到print的时候,才会将n的值带入 此时n = 10