一、列表生成式:可以动态生成列表,而不是用固定值给列表赋值,这样程序会更灵活
def test(i):
# 取偶数
if i % 2 == 0:
return i
# 普通的生成式
list1 = [i * 2 for i in range(10)]
print(list1)
# 通过一个函数选择值
list1 = [test(i) for i in range(10)]
print(list1)
二、生成器:列表在调用之前已经准备好了,如果这个列表的数据量巨大,那么就一直占用内存并且效率低,
比如有一个100万元素的列表,本次循环只循环到前面100个,后面的元素没有用到,有没有一种
方式可以在调用的时候再生成元素呢?这就是生成器。
# (i * 2 for i in range(10000)) 返回一个生成器的内存地址
list3 = (i * 2 for i in range(10000))
注意:生成器不能用“list[0]”这种方式调用里面的元素,因为此时元素还没有生成,只能用for循环来访问
# 可以用__next()__方法来取得下一个,取得下一个的同时前面一个元素就消失了,所以生成器只记录当前位置的元素。不能往后退,往前用__next()__
print(list3.__next__())
print(list3.__next__())
print(list3.__next__())
# 一般都是用循环来访问,不会用到__next__()方法
# 这种方式就是使用了python内置的迭代器,里面内置有next方法
for i in list3
print(i)
举个例子,将斐波那契数列用生成器来生成,(斐波那契数列定义:除了第一个和第二个数,后面的数都是前面两个数字想加得到)
下面普通写法
list_num = (i for i in range(10))
list_new_num = []
for i in list_num:
if i != 0 and i != 1:
list_new_num.append(list_new_num[i - 1] + list_new_num[i - 2])
else:
list_new_num.append(i)
print(list_new_num[i])
斐波那契数列的另外一种写法
n, a, b = 0, 0, 1 # 连续赋值
while n < 10:
print(b)
a, b = b, a + b
n = n + 1
将上面的代码改一下。
通过yield关键词保存函数的中断状态,外面调用的代码可以先执行,不需要等到所有值都计算出来了再执行,这样可以大大提高效率
执行到next()的时候再返回yield的位置继续执行函数内部的代码。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 加上yield就变成一个生成器了,相当于保存了b变量此处的值。这个b是要传到外面的,所以用yield标记
a,b = b,a+b
n = n+1
return "出错了" # 这是错误信息,将会被异常捕获。
# 返回一个生成器,所以fib相当于这个生成器的具体实现。
g = fib(6)
while True:
try:
# 这个next是一个内置方法,作用跟__next()__一样,取出生成器的下一个元素
# next后进入上面fib函数的yield的位置
x = next(g)
print("g:",x)
except StopIteration as e: # 这里捕捉异常来结束程序
print("error:",e.value)
break
yield 还可以用做单线程下的协程的调用,虽然仍然是单线程(线程和协程将在后面单独介绍),但是通过yield能大大提高效率
以下是一个典型的生产消费模型,一个人生产,若干人消费,而且是同时进行,也是利用了yield的特性,我们称为“并行生成器”
import time
def consumer(name):
print("%s准备吃包子了!"%name)
while True:
baozi = yield # yield无返回值,为什么赋值给baozhi呢?
print("包子[%s]来了,被[%s]吃了。" % (baozi,name))
# c = consumer("tangwei")
# c.__next__() # 第一个__next__执行到consumer函数中的yield的位置,然后返回
# c.__next__() # 第二个__next__继续执行剩下的部分一直到循环继续来执行到yield跳出
# c.__next__() # 第一个__next__执行到consumer函数中的yield的位置,然后返回
# c.send("肉馅") # send将一个值传递给consumer函数内的yield的位置,也就是赋值给变量baozi然后next,仅调用__next__不会给yield传递值
def producer(name):
# 生成1个生成器
c1 = consumer("tangwei")
# 生成1个生成器
c2 = consumer("chenyadan")
# c1准备吃包子,__next__执行到consumer函数中的yield的位置,然后返回
c1.__next__()
# C2同上
c2.__next__()
print("我是厨师[%s],开始做包子了..."%(name))
for i in range(10):
time.sleep(1)
print("做了一个包子,一人一半..")
c1.send(i) # send将一个值传递给consumer函数内的yield的位置,也就是赋值给变量baozi,仅调用__next__不会给yield传递值
c2.send(i)
# 虽然程序依然是顺序执行的,但是因为程序流可以在函数之间跳转,就无需等到一个函数结束以后再执行另外一个函数,这样大大提高了执行效率。
# 这是单线程下的并行效果,也就是协程,协程是比线程更小的单位,寄生在线程中。
producer("狗不理")