VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • Python学习笔记:生成器(Generator)详解

一、列表生成式:可以动态生成列表,而不是用固定值给列表赋值,这样程序会更灵活
复制代码
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("狗不理")
复制代码


相关教程