列表生成式
灵活度更高
list1 = [i * 2 for i in range(10)]
print(list1)
生成器
特点:只有在调用时才会生成相应的数据
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
list1 = [i * 2 for i in range(10)] #列表
print(list1)
generator = (i * 2 for i in range(10)) #生成器
print(generator) # 生成器
运行结果
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
<generator object <genexpr> at 0x0000019391C45938>
创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
生成器独有的next方法|当__next__取完之后,如果继续则会报错
print(generator.__next__())
print(generator.__next__())
我们讲过,generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:
单线程
实现斐波那契数列
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield (b) # 生成器
a, b = b, a + b
n = n + 1
return 'done'
# fib(10)
print(fib(10))
fib_gen = fib(10)
print(fib_gen.__next__())
print(fib_gen.__next__())
print(fib_gen.__next__())
#取完之后,则会报错
捕获异常
def fib(max):
n, a, b = 0, 0, 1
while n < max:
# print(b)
yield (b) # 生成器|保存函数的中断状态|
a, b = b, a + b
n = n + 1
return '-----done-----'
f = fib(10)
while True:
try:
x = next(f)
print('f:', x)
except StopIteration as e:
print('Generator return value:', e.value)
break
还可通过yield实现在单线程的情况下实现并发运算的效果(最简单的xiecheng)
单线程下的并行效果
import time
def consumer(name):
print("%s 准备吃包子啦!" % name)
while True:
baozi = yield
print("包子[%s]来了,被[%s]吃了!" % (baozi, name))
def producer(name):
c = consumer('A')
c2 = consumer('B')
c.__next__()
c2.__next__()
print("老子开始准备做包子啦!")
for i in range(10):
time.sleep(1)
print("做了2个包子!")
c.send(i)
c2.send(i)
producer("alex")