1.列表生成式
需求:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 要求你把列表里的每个值加1
方式一:
>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>>> a[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>>> for index,i in enumerate(a):... a[index] += 1...>>> a[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
方式二:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]a = list(map(lambda x:x+1, a))print(a)# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
方式三: 列表生成式
>>> a = [i+1 for i in range(10)]>>> a[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]>>>
2.python生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的
[]
改成(),
就创建了一个generator:
>>> L = [x*x for x in range(10)]>>> L[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]>>> g = (x*x for x in range(10))>>> gat 0x000000000261B258>
创建
L
和g
的区别仅在于最外层的[]
和()
,L
是一个list,而g
是一个generator。
我们可以打印generator 中的每个元素 ,next(g)
>>> next(g)0>>> next(g)1>>> next(g)4>>> next(g)9>>> next(g)16>>> next(g)25>>> next(g)36>>> next(g)49>>> next(g)64>>> next(g)81>>> next(g)Traceback (most recent call last): File "", line 1, in StopIteration
generator 保存的是算法,每次调用next(g)
计算并返回每个元素值,,直到最后一个元素后再next(g)
,会报StopIteration 的错;
因为generator是可迭代对象,所以我们可以使用 for 循环来处理:
>>> g = (x*x for x in range(10))>>> for i in g:... print(i)...0149162536496481>>>
我们可以看到,for循环 g 不会在最后报错
其实,range() 就是一个生成器
>>> range(1000000000)range(0, 1000000000)>>>
range(1000000000)
后,并没有打印出生成的列表,而是生成了一个公式算法,并没有生成这个列表,在我们真正使用的时候才会不断生成,当然这是在python3 中。
在python2中,range(10)
后,会把列表直接打印出来,但是python2.7中有一个 xrange(10)
,和python3中的 range()
是一样的
用函数写一个生成器
#斐波那契数列def fib(max): n, a, b = 0, 0, 1 while n < max: print(b, end=" ") a, b = b, a + b n += 1 return 'done'fib(10) # 1 1 2 3 5 8 13 21 34 55
改动一下,把print改为yield
def fib(max): n, a, b = 0, 0, 1 while n < max: print("bofore yield......") yield b print("after yield.......") a, b = b, a + b n += 1 return 'done'f = fib(10) # turn fuction into a generator# 无打印结果,未执行
next(f) # bofore yield......
再 next(f)
一下,
即
next(f)next(f)
打印结果:
# bofore yield......# after yield.......# bofore yield......
注意:
yield b
,把函数的执行过程冻结在这一步,并且把b的值 返回到外面的 next(f),这里可以把函数里面每一步的值返回出来,这一点很有用,在一些场景中可以利用- 函数只要加 yidld,函数就会冻结,不再执行,然后生成一个生成器对象
def range2(n): count = 0 while count < n: print('count:',count) count += 1 yield count # print('------') return 'end........'new_range = range2(3)n1 = next(new_range)n2 = next(new_range)n3 = next(new_range)n4 = next(new_range)print(n3)print(n4)
打印结果:
Traceback (most recent call last): File "D:/Python/python_learning/luffy/code/part2/函数/生成器4.py", line 18, inn4 = next(new_range)StopIteration: end........count: 0count: 1count: 2------
注意:只要函数中有 yield ,
1.函数名加() 就得到了一个生成器,
2.return在生成器中,代表生成器的终止,
3.在运行到最后的时候,就会报出异常,此时,return的内容会以 StopIteration: end........
的异常方式反馈
def range2(n): count = 0 while count < n: print('count:',count) count += 1 # yield count sign_str = yield count print('sign_str:',sign_str) print('------') return 'end........'new_range = range2(3)n1 = next(new_range)print("do someting else...")new_range.send("stop")
打印:
count: 0do someting else...sign_str: stopcount: 1
1.send,唤醒并继续执行
2.发送一个信息到生成器内部
我们可以根据send到生成器内部的内容做一些事情,比如:
def range2(n): count = 0 while count < n: print('count:',count) count += 1 # yield count sign_str = yield count if sign_str == 'stop': break return 'end........'new_range = range2(3)n1 = next(new_range)print("do someting else...")new_range.send("stop")
打印:
Traceback (most recent call last): File "D:/Python/python_learning/luffy/code/part2/函数/生成器6.py", line 19, innew_range.send("stop")StopIteration: end........count: 0do someting else...
newt(new_range)
实际上是发了一个 'None' 到生成器内部