生成器

1、Python 中提供的 生成器:

  本质我们自己写的能实现迭代器功能的东西,就叫生成器。

  1.1生成器函数:

  常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果。在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行

  1.2.生成器表达式:

  类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

2、生成器Generator:

  本质:迭代器 ( 所以自带了 __iter__方法和 __next__方法,不需要我们去实现)

  特点:惰性运算,开发者自定义

3、生成器函数:

  划重点:

    ① 必须含有yield关键字

      ② 执行之后会得到一个生成器对象,作为返回值

def generator():
    print('123')
    yield 'ab'
ret = generator()
print(ret)           # <generator object generator at 0x000001DA911EC3B8>
print(ret.__next__())     # 第一次执行函数体内的代码,如上①输出123 ②return ab
def wahaha():
    for i in range(2000):
        yield '娃哈哈%s'%i
g = wahaha()
count = 0
for i in g:        # 循环迭代输出
    count += 1
    print(i)
    if count > 100:
        break
print('*********',g.__next__())   #********* 娃哈哈101

4、field关键字

  4.1 yield和return的区别

# 在一个函数里return只能执行一次,return之后函数就彻底结束了:
    def test_return():
        return 1
    return 2  # 永不执行
    return 3  # 永不执行

    def test_return2():
        for i in range(10):
            return i  # 只能返回0,函数就结束了

——————————————————————————————————————

# yield之后可以保存函数的运行状态,下次继续执行:
在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
def test_yield(): yield 1 yield 2 # 下次next()后执行 yield 3 # 下次next()后执行 def test_yield2(): for i in range(10): yield i # 每调用一次next()就会依次弹出0~9

  4.2 yield的作用

# ① yield把函数变成了生成器(生成器就是迭代器)。
# ② 为函数封装好了__iter__和__next__方法,把函数的执行结果做成了迭代器。
# ③ 遵循迭代器的取值方式 — obj.__next__(),触发的是函数的执行。函数暂停与继续执行的状态都是由yield保存的。
# 倒计时的例子

def countdown(n):
    print("倒计时开始")
    while n > 0:
        yield n
        n -= 1
    print("发射")

g = countdown(5)
print(g.__next__())  # 打印"倒计时开始" 返回5 (此时n=5)
print(g.__next__())  # 返回4 (此时n=4)
print(g.__next__())  # 返回3 (此时n=3)
print(g.__next__())  # 返回2 (此时n=2)
print(g.__next__())  # 返回1 (此时n=1)
print(g.__next__())  # 打印"发射" 抛出StopIteration异常(此时n=0)

  4.3 yield from:在一个生成器中引用另外一个生成器

  yield from后面需要加的是可迭代对象,它可以是普通的可迭代对象,也可以是迭代器,甚至是生成器。

  详细查看  https://www.cnblogs.com/wongbingming/p/9085268.html

def generator():
    a = '12312312'
    b = 'asdhughiwu'
    yield from a
    yield from b
g = generator()
for i in g:
    print(i)

# 运行结果:
1
2
3
1
2
3
1
2
a
s
d
h
u
g
h
i
w
u
yield from
astr='ABC'       # 字符串
alist=[1,2,3]    # 列表
adict={"name":"wangbm","age":18}   # 字典
agen=(i for i in range(4,8))    # 生成器

def gen(*args, **kwargs):
    for item in args:
        yield item    # 此处返回的是'ABC',[1,2,3],{"name":"wangbm","age":18},<generator object <genexpr> at 0x000001D0A010C3B8>
        yield from item    # 相当于 for i in item:yield i

new_list=gen(astr, alist, adict, agen)
print(list(new_list))


# 运行结果:
# ['ABC', 'A', 'B', 'C', [1, 2, 3], 1, 2, 3, {'name': 'wangbm', 'age': 18}, 'name', 'age', <generator object <genexpr> at 0x00000256B8D6C3B8>, 4, 5, 6, 7]

5、send关键字

  yield可以返回值,也可以接收值。

  通过生成器的send方法可以给yield传值。

# 必须先执行__next__,才能执行send()
# send两个作用:
    # 1.给yield传值  
    # 2.继续执行函数

def eat(name):
    print('%s要开始吃了!' % name)
    while 1:
        food = yield
        print('{0}在吃{1}'.format(name, food))

a = eat('alex')
a.__next__()  # 初始化,让函数暂停在yield处
a.send('包子')  
a.send('饺子')

6、生成器表达式

  详见:https://www.cnblogs.com/timetellu/p/10677743.html

  把列表解析的[ ]换成( )得到的就是生成器表达式:

sum(x ** 2 for x in range(4))

# 不用多此一举构造一个列表
# sum([x ** 2 for x in range(4)]) 

7、生成器相关面试题

# 只能取一次值

def demo():
    for i in range(10):
        yield i
g = demo()

g1 = (i for i in g)
g2 = (i for i in g1)
print(list(g1))      # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(list(g2))      # [ ]
面试题1
def add(n,i):
    return n+i
def test():
    for i in range(4):   # [0,1,2,3]
        yield i
        
g = test()
for n in [1,10]:
    g = (add(n,i) for i in g)
    
print(list(g))    #到list(g)才执行,向上寻找g,取得是for循环最后一步的执行结果:
                        # 执行g = (add(n,i) for i in (add(n,i) for i in test()))  (此时n=10)

运行结果:
[20, 21, 22, 23]

### for循环套生成器表达式的,执行过程拆开
# n = 1时,
# g0 = (add(n,i) for i in g)   
# n = 10时,
# g1 = (add(n,i) for i in g0)   

# g = (add(n,i) for i in (add(n,i) for i in test()))   此时n = 10
# g = (add(10,i) for i in (10,11,12,13))
面试题2
原文地址:https://www.cnblogs.com/timetellu/p/10681281.html