python 生成器

生成器(generator)

     在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。生成器本身就是迭代器。

1 生成器表达式

生成器表达式的语法和列表生成式的一样,只不过把列表生成式的[]换成()。

gen=(x+y for x in ("A","B","C") for y in ("a","b","c"))
print(gen)
print(next(gen))
print(next(gen))

result:
<generator object generator at 0x00000203D50FF468>
Aa
Ab

2 生成器函数

在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器函数。

yield的作用:

  1. yield把函数变成生成器(把__iter__()和__next__()封装到函数里)
  2. yield能让函数挂起,并保存函数当前的运行状态。
  3. yield能返回值(这一点和return类型)

yield和return的区别:
return返回一次值,函数就结束运行。而yield能多次返回值。

  • 有了yield这样灵活的语法,我们就能自己定义生成器了。
from collections import Iterator
def func(n):
    i=0
    while i<=n:
        yield i
        i+=2
g=func(10)
if isinstance(g,Iterator):
    print(next(g),end=" ")
    print(next(g),end=" ")
    print(next(g),end=" ")
    print(next(g),end=" ")
    print(next(g),end=" ")
    print(next(g),end=" ")
    print(next(g),end=" ")
else:
    print(g)


result:
Traceback (most recent call last):
0 2 4 6 8 10   File "D:/devtools/workspace/python/PycharmProjects/py_fulstack_s4/day23/yield用法.py", line 19, in <module>
    print(next(g),end=" ")
StopIteration

每次运行遇到yield先执行再挂起,并可以通过next()方法来获取其返回值,然后继续向下运行,直到遇到下一个yield挂起并返回。直到遇到StopIteration停止。

改用for遍历:

if isinstance(g,Iterator):
    for i in g:
        print(i, end=" ")
else:
    print(g)
  • 每一次调用生成器函数返回一个生成器对象,调用同一个生成器函数返回的多个生成器对象互相之间不影响。
def func():
    pass
a=func()
b=func()
print(a is b)   #True
def generator():
    yield 1
g=generator
print(g)   #<function generator at 0x0000020A119E3EA0>
g1=generator()
print(g1)   #<generator object generator at 0x0000020A11E2F410>
g2=generator()
print(g2)   #<generator object generator at 0x0000020A11E2F1A8>
print(g1 is g2)   #False

g是函数生成器的地址,而g1和g2是生成器对象的地址。可以看出同一个函数生成器调用后赋值给多个变量,这些变量是互不受影响的。

  • 如何接收另一个生成器函数的迭代。通过from关键字来实现
def counter(start=0):
    while True:
       yield start
       start += 1

def gen():
    yield 1
    yield 2
    yield from counter(10)

g=gen()

for i in range(10):
    print(next(g),end=" ")

结果:

1 2 10 11 12 13 14 15 16 17 

3 生成器常用的方法

  • close() 关闭生成器,关闭以后再调用next()方法会报出StopIteration。
li=(x for x in range(10) if x%2==0)
print(next(li))
print(next(li))
li.close()
print(next(li))

result:
Traceback (most recent call last):
  File "D:/devtools/workspace/python/PycharmProjects/py_fulstack_s4/day23/yield用法.py", line 14, in <module>
    print(next(li))
StopIteration
0
2
  • throws()用来向生成器函数送入一个异常,可以结束系统定义的异常,或者自定义的异常。
    throw()后直接抛出异常并结束程序,或者消耗掉一个yield,或者在没有下一个yield的时候直接进行到程序的结尾。
def gen():
    while True:
        try:
            yield "value 1"
            yield "value 2"
            print("here!")
        except ValueError:
            print("the ValueError is here")
        except TypeError:
            break
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(g.throw(TypeError))

result:

value 1
the ValueError is here
Traceback (most recent call last):
value 1
value 2
  File "D:/devtools/workspace/python/PycharmProjects/py_fulstack_s4/day23/gen.py", line 18, in <module>
    print(g.throw(TypeError))
StopIteration

执行过程:

  1. print(next(g)):输出value 1,并挂起。
  2. 由于执行了g.throw(ValueError),所以会跳过所有后续的try语句,也就是说yield ‘value 2’不会被执行,然后进入到except语句,打印出the ValueError is here。然后再次进入到while语句部分,消耗一个yield,所以会输出value 1。
  3. print(next(g)),会执行yield ‘alue 2’语句,并挂起。
  4. g.throw(TypeError):会跳出try语句,从而print(‘here!’)不会被执行,然后执行break语句,跳出while循环,然后到达程序结尾,所以跑出StopIteration异常。

5 yeild在协程中的使用

send()接受外部传入的一个变量,并根据变量内容计算结果后返回接受外部传入的一个变量,并根据变量内容计算结果后返回

def func(name):
    print("%s 开始点餐!"%(name))
    food_menu=[]
    while True:
        food=yield food_menu
        food_menu.append(food)
        print("%s 的菜单是:%s"%(name,food_menu))
    print("完成点餐!")
g=func("egg")
print(next(g))
print(g.send("西红柿炒番茄"))
print(g.send("白菜炒包菜"))


result:
egg 开始点餐!
[]
egg 的菜单是:['西红柿炒番茄']
['西红柿炒番茄']
egg 的菜单是:['西红柿炒番茄', '白菜炒包菜']
['西红柿炒番茄', '白菜炒包菜']
  1. g.send(None)或者next(g)可以启动生成器函数并执行到第一个yield语句结束的位置。并执行到第一个yield语句结束的位置。此时food_menu没有值,所以返回为[]。如果去掉next(g),程序会报错TypeError: can't send non-None value to a just-started generator。
  2. 当运行代码g.send("西红柿炒番茄"),实际上是把"西红柿炒番茄"赋值给food,而g.send()的返回值就是yield后面跟着的food_menu。输出结果:['西红柿炒番茄']
  3. 重复第2步,输出:['西红柿炒番茄', '白菜炒包菜']。

其他生成器链接:http://www.cnblogs.com/huxi/archive/2011/07/14/2106863.html

原文地址:https://www.cnblogs.com/yangzhenwei123/p/6759232.html