https://www.cnblogs.com/yuanchenqi/articles/5769491.html
1. 列表生成式
我现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],我要求你把列表里的每个值加三次方,你怎么实现?你可能会想到2种方式
1 a=[0,1,2,3,4,5,6,7,8,9] 2 b=[] 3 for i in a: 4 b.append(i+1) 5 a=b 6 print(a)
1 a=map(lambda x:x+1,range(10)) 2 print(a,type(a)) 3 for i in a: 4 print(i) 5 6 输出结果: 7 <map object at 0x0272A4D0> <class 'map'> 8 1 9 2 10 3 11 4 12 5 13 6 14 7 15 8 16 9 17 10
1 (1) 2 t=[0,1] 3 a,b=t 4 print(a) 5 print(b) 6 输出结果: 7 0 8 1 9 (2) 10 t=[0,1,2] 11 a,b=t 12 print(a) 13 print(b) 14 输出结果: 15 ValueError: too many values to unpack (expected 2) 16 (3) 17 t=[0,1] 18 a,b,cc=t 19 print(a) 20 print(b) 21 print(c) 22 输出结果: 23 ValueError: not enough values to unpack (expected 3, got 2)
1 def f(n): 2 return n**3 3 a=[f(x) for x in range(10)] 4 print(a,type(a)) 5 6 等价于 7 a=[x**3 for x in range(10)] 8 print(a,type(a)) 9 输出结果: 10 [0, 1, 8, 27, 64, 125, 216, 343, 512, 729] <class 'list'>
2. 生成器(generator)
生成器的创建方式有两种:()和yield
1 a=(x**3 for x in range(10)) 2 print(a,type(a)) 3 4 等价于 5 def f(n): 6 return n**3 7 a=(f(x) for x in range(10)) 8 print(a,type(a)) 9 10 输出结果: 11 <generator object <genexpr> at 0x02DC34B0> <class 'generator'>
生成器数据类型的变量指向的内存地址,根本就没有任何数据在内存中,也没有存储在任何地方。
(类比理解:列表是将数据存储在变量指向的内存空间的,这就相当于你冰箱里面放了十道菜,你想吃哪个就去拿,这是占用一定的空间的,为了解决这个问题,我们有了生成器,就好比你拥有了一个有脾气的厨师,让厨师给你现做现吃,做哪个你就吃哪个好了,在生成器s中可以用生成器内置方法s.__next__()或者python内置函数next(s)这个方法按顺序生成值,这取决于你生成器的本身的特性。建议使用next(s))
生成生成器对象里面的值
1 a=(x*2 for x in range(10)) 2 print(a,type(a)) 3 print(a.__next__()) 4 print(next(a)) 5 print(next(a)) 6 print(next(a)) 7 输出结果: 8 <generator object <genexpr> at 0x028B34B0> <class 'generator'> 9 0 10 2 11 4 12 6
注:生成器就是一个可迭代对象,迭代器也是可迭代对象
1 a=(x*2 for x in range(4)) 2 print(a,type(a)) 3 4 for i in a: 5 print(i) 6 7 输出结果: 8 0 9 2 10 4 11 6
注:(1)for循环可遍历可迭代对象,有__iter__方法的都是可迭代对象内部的方法,iter()是内置函数
(2)for循环遍历可迭代对象的三个步骤:
-
-
- 调用可迭代对象的iter方法,返回一个迭代器对象
- 不断调用迭代器的next方法
- 处理stopiteration异常
-
while:
try:
i=next(generator)
except StopIteration
break
(3)当内存中的数据,没有变量引用的时候(即没有变量指向该内存地址),该数据就会被python的解释器当垃圾清除掉
这样就算遍历生成器,也不会占用大量内存,因为i的指向一直在更新,没有被引用的数据就会被清除
1 def f(): 2 print('Hello') 3 yield 1 4 print('Ok') 5 yield 2 6 g=f() 7 print(g,type(g)) 8 输出结果: 9 <generator object f at 0x033634B0> <class 'generator'>
注:没有yield就是普通的函数,有yeild,定义的函数f()就是一个生成器对象,有几个yield就相当于几道菜。
生成生成器对象里面的值
1 def f(): 2 print('Hello') 3 yield 1 4 print('Ok') 5 yield 2 6 g=f() 7 next(g) 8 next(g) 9 输出结果: 10 Hello 11 Ok
注:
(1)每调用一次next(g),就执行一遍g函数,以yield为结束点,返回1(相当于return结束函数的运行),再执行一遍next(g),以上次的结束点为起点,继续执行g函数,并返回2
(2)可通过yield实现在单线程的情况下实现并发运算的效果,即伪并发,CPU切换速度特别快,以至于我们不觉得有切换这一过程
1 def f(): 2 print('Hello') 3 yield 1 4 print('Ok') 5 yield 2 6 g=f() 7 8 for i in g: 9 print(i) 10 输出结果: 11 Hello 12 1 13 Ok 14 2
注:for循环遍历生成对象g,以yield结束标志执行函数内容,并打印出了yield返回值
1 def fibo(N): 2 Max=N 3 n,before,after=0,0,1 4 while n<Max: 5 # print(after) 6 yield after 7 before,after=after,before+after 8 n+=1 9 g=fibo(8) 10 # print(next(g)) 11 # print(next(g)) 12 # print(next(g)) 13 for i in g: 14 print(i)
生成器对象的send方法
1 def fibo(N): 2 Max=N 3 n,before,after=0,0,1 4 while n<Max: 5 # print(after) 6 count=yield after 7 print(count) 8 before,after=after,before+after 9 n+=1 10 g=fibo(8) 11 12 g.send(None) #相当于next(g),先进入生成器对象,遇到yield,返回after后结束 13 g.send('eee') #从上一次结束点开始,继续程序,将send的参数赋值给count,继续执行后面的语句,直到再次遇到yield 14 输出结果: 15 eee
注:进入生成器后(next,send(None))再能send传参数
3. 迭代器(iterator)
生成器都是迭代器,迭代器不一定是生成器;
可迭代对象(Iterable)不一定是迭代器(Iterator),通过iter方法可以把可迭代对象变成迭代器(迭代器对象),这是因为可迭代对象里面有__iter__方法。
什么是迭代器:需要满足两个条件:(1)有iter方法(2)有next方法
1 from collections import Iterable,Iterator 2 d=[1,2,3,4,5,6] 3 a=iter(d) 4 print(a,type(a)) 5 # print(next(a)) 6 # # print(next(a)) 7 # for i in a: 8 # print(i) 9 print(isinstance(d,Iterable)) #判断d是不是可迭代对象 10 print(isinstance(d,list)) #判断d是不是列表 11 print(isinstance(d,Iterate)) #判断的是不是迭代器