python生成器,,生成器(genera


生成器(generator)指代的是生成器对象,它可以由生成器表达式得到,也可以使用yield关键字得到一个生成器函数,调用这个生成器函数就可以得到一个生成器对象。另外,通过数据的转换也可以获取生成器。

生成器对象是一个可迭代对象,它是一个迭代器。生成器的作用是延迟计算,惰性求值。

生成器表达式产生生成器对象

#生成器表达式产生生成器a =(x**2 for x in range(5))print(type(a))print(next(a))print(next(a))print(next(a))print(next(a))结果为:<class ‘generator‘>0149

生成器函数

生成器函数也就是函数体中包含yield关键字的函数,这个函数会返回一个生成器对象!

def inc():    for i in range(5):        yield i print(type(inc))print(type(inc()))x=inc()print(type(x))print(next(x))for m in x:    print(m,"**")for i in x:#因为x为生成器对象,所以它只能被遍历一次。再次遍历不会被执行了。    print(i,"*")结果为:<class ‘function‘><class ‘generator‘><class ‘generator‘>01 **2 **3 **4 **

由上面的例子可以发现,普通的函数调用,函数会立即执行完毕,但是生成器函数不会,它可以使用next函数执行多次,直到最后。

def func():    print("111")    yield 222gener = func() ret = gener.__next__()#print(ret)结果为:111

而生成器函数等价于生成器表达式,只不过生成器函数可以更加的复杂。比如上面的生成器函数可以写成下面的生成器表达式。

y = (i for i in range(5))print(type(y))print(next(y))print(next(y))结果为:<class ‘generator‘>01

在生成器函数中,可以使用多个yield语句,执行一次后会暂停执行,然后把yield表达式的值返回。函数如果再次执行的话,会执行到下一个yield语句,在生成器函数中, return依然可以终止程序的运行,单return语句的返回值不能被获取到。

在生成器函数中,return会导致无法继续获得下一个值,这个时候会抛出stopiteration异常,而如果函数没有显示的return语句,如果生成器函数执行到结尾,一样会抛出stopiteration异常。

def gen():    print("line 1 ")    yield 1    print("line 2")    yield 2    print("line 3")    return 3next(gen())#line 1next(gen())#line 1都是line1因为这不是同一个生成器对象,第二次执行函数,又返回了一个生成器对象。g =gen()print(next(g))print(next(g))print(next(g))结果为:line 1 line 1 line 1 1line 22line 3---------------------------------------------------------------------------StopIteration                             Traceback (most recent call last)<ipython-input-16-3c0007202311> in <module>     13 print(next(g))     14 print(next(g))---> 15 print(next(g))StopIteration: 3def gen():    print("line 1 ")    yield 1    print("line 2")    yield 2    print("line 3")    return 3next(gen())next(gen())g =gen()print(next(g))print(next(g))print(next(g, ‘End‘)) #没有元素了,给个缺省值print(next(g, ‘End‘)) 结果为:line 1 line 1 line 1 1line 22line 3EndEnd

所以,生成器函数就是包含yield语句的函数,而生成器函数生成“生成器”的时候,生成器函数的函数体并不会立即执行,它需要next函数,有点拨一下转一下的意思,next(generator)会从函数的当前位置向后执行到碰到的第一个yield语句,这个时候有值会弹出值,并且暂停函数的执行。

再次调用next函数,重复上面的步骤。,而如果没有多余的yield语句被执行,如果再次调用next函数,就会抛出stopiteration异常。

生成器应用

现在有一个需求,比如我像一个服装厂订购了100件衣服,这个时候服装厂全部给我生成好了。比如下面这个代码。

def cloth():    lst = []    for i in range(10):        lst.append("cloth"+str(i))    return lstcloth()结果为:[‘cloth0‘, ‘cloth1‘, ‘cloth2‘, ‘cloth3‘, ‘cloth4‘, ‘cloth5‘, ‘cloth6‘, ‘cloth7‘, ‘cloth8‘, ‘cloth9‘]

但是,现在我还没有那么多的学习,一次性给我那么多,我根本没法用。所以,最好的我要一件,你给我一件。这个时候就需要用到生成器了。

def cloth():    lst = []    for i in range(10):         yield "cloth"+str(i)a = cloth()print(a.__next__())print(a.__next__())print(a.__next__())结果为:cloth0cloth1cloth2

所以,上面的第一种就是一次性全部拿出来,会占用内存,第二种生成器,一次就一个,用多少生成多少,用next函数一个一个的拿,然后指向下一个。

生成器还可以用在诸如无限循环,计算器,处理递归等问题上,应用最广泛的是协程。

#生成器处理无限循环,就不会陷入死循环,它拨一下,转一下。def counter():    i = 0    while True:        i+=1        yield idef inc(a):    return next(a)c = counter()print(inc(c))print(inc(c))for i in range(10):    print(inc(c))结果为:123456789101112

应该注意的是,如果上面的代码写成下面这样,每次就会产生一个新的“生成器”,达不到拨一下转一下的效果。

def counter():    i = 0    while True:        i+=1        yield idef inc():    a = counter()    return next(a)print(inc())print(inc())print(inc())结果为:111

下面用生成器配合无限循环写一个计数器。

def inc():    def counter():        i = 0        while True:            i+=2            yield i    c = counter()    return lambda :next(c)foo = inc()print(foo())print(foo())结果为:24

这个例子和上面无限循环差不多,都是一个意思,只是换了一种写法,同时这个例子用到了lambda表达式,而return返回的是一个匿名函数。它也等价于下面的代码:

def inc():    def counter():        i = 0        while True:            i+=2            yield i    c = counter()    def _inct():        return next(c)    return _inctfoo = inc()print(foo())print(foo())print(foo())

生成器同样可以用来处理递归的问题,比如下面这个例子实现斐波拉契数列。可以想生成多少就生成多少。

def fib():    x = 0    y = 1    while True:        yield y        x,y=y,x+yfoo = fib()for _ in range(5):#生成5个    print(next(foo))for _ in range(5):#再生成5个    print(next(foo))结果为:11235813213455

上面的这段代码等价于下面的这段代码。

pre = 0 cur = 1print(pre,cur,end=" ")def fib1(n,pre = 0,cur=1):    pre,cur=cur,pre+cur    print(cur,end=" ")    if n ==2:        return    fib1(n-1,pre,cur)fib1(11)结果为:0 1 1 2 3 5 8 13 21 34 55 89 

生成器还有一个高级用法,那就是协程coroutine,协程比进程、线程轻量级,它是在用户空间调度函数的一种实现,在Python 3 中,asyncio就是协程实现,已经加入到标准库,同时在Python3.5中,使用async\await关键字直接原生支持协程。

协程调度器实现原理为:有两个生成器A,B,next(a)后,A执行到了yield语句暂停,然后去执行next(B),B执行到yield语句也暂停,然后再次调用next(A),再调用next(B),周而复始,就实现了调度的效果。

同时应该注意,协程是一种非抢占式的调度。

yield from

yield from?是Python3.3出现的新语法,yield from iteratle 是 for item in iterable:yield item形式的语法糖。

def inc():    for i in range(1000):        yield ifoo = inc()print(next(foo))print(next(foo))print(next(foo))def inc():    yield from range(1000)foo = inc()print(next(foo))print(next(foo))print(next(foo))结果都为:012

上面的两端代码其实是等效的。

再比如下面这个例子,从一个可迭代对象中一个个拿元素。

def counter(n):     for x in range(n):        yield xdef inc(n):    yield from counter(n)foo = inc(10)print(next(foo))print(next(foo))print(next(foo))结果为:012

python生成器

评论关闭