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))
>>> g
<generator object <genexpr> at 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 "<stdin>", line 1, in <module>
StopIteration
generator 保存的是算法,每次调用next(g)
计算并返回每个元素值,,直到最后一个元素后再next(g)
,会报StopIteration 的错;
因为generator是可迭代对象,所以我们可以使用 for 循环来处理:
>>> g = (x*x for x in range(10))
>>> for i in g:
... print(i)
...
0
1
4
9
16
25
36
49
64
81
>>>
我们可以看到,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, in <module>
n4 = next(new_range)
StopIteration: end........
count: 0
count: 1
count: 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: 0
do someting else...
sign_str: stop
count: 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, in <module>
new_range.send("stop")
StopIteration: end........
count: 0
do someting else...
newt(new_range)
实际上是发了一个 'None' 到生成器内部