6.3. 生成器

6.3.1. yield语句

  • 每次返回一个值,有点类似于return语句

  • 冻结执行,程序每次执行到yield语句就会被暂停

return 语句一次返回所有数据,函数调用结束;而yield语句只返回一个元素数据,函数调用不会结束,只是 暂停,直到__next__()方法被调用,程序继续执行yield语句之后的语句代码。

6.3.2. 快速定义一个生成器

#!/usr/bin/env python
#-*- coding:utf8 -*-

a=(i for i in range(5))
print(a)
#生成器用法
print(type(a))
print(list(a))

代码示例1

#!/usr/bin/env python
#-*- coding:utf8 -*-

def myYield(n):
    '''
    定义一个生成器(03.函数)
    '''
    while n>0:
        print("开始生成......")
        yield n
        print("完成一次......")
        n -= 1
if __name__ == '__main__':
    for i in myYield(4):
        print("遍历得到的值:",i)
    print()

    my_yield = myYield(3)      #生成一个生成对象
    print("已经实例化生成器对象")
    my_yield.__next__()
    print("第二次调用__next__()方法:")
    my_yield.__next__()

代码示例2

#!/usr/bin/env python
#-*- coding:utf8 -*-

def myYield(n):
    '''
    :return:rcv用来接收调用者传来的值
    '''
    while n >0:
        rcv = yield n       #rcv用来接收调用者传来的值
        n -=1
        if rcv is not None:
            n = rcv

if __name__ == '__main__':
    my_yield = myYield(3)
    print(my_yield.__next__())
    print(my_yield.__next__())
    print("传给生成器一个值,重新初始化生成器")
    print(my_yield.send(10))
    print(my_yield.__next__())

代码示例3

#!/usr/bin/env python
#-*- coding:utf8 -*-

#通过生成器推导式构建

def generator():
    print(123)
    content = yield 1
    print("===========",content)
    print(456)
    yield 2

g = generator()
ret = g.__next__()
print("*****",ret)

ret = g.send("hello")
print("****",ret)


print()


def func1():
    print(11)
    print(333)
    yield 222
    print(666)
    yield 777

g_obj = func1()
print(g_obj.__next__())
print(g_obj.__next__())

eg

#!/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/5/17 23:21
# filename: 生成器test.py
def square(num):
    for i in range(1, num + 1):
        yield i * i


# for i in square(5):
#     print(i, end=" ")
hujianli = square(5)
print(hujianli.__next__())
print(hujianli.__next__())
print(hujianli.__next__())
print(hujianli.__next__())
print(hujianli.__next__())
print(hujianli.__next__())
生成器函数通过yield返回数据,与return不同的是,
return语句一次返回所有数据,函数调用结束;
而yield语句只返回一个元素数据,函数调用不会结束,只是暂停,
直到__next__()方法被调用,程序继续执行yield语句之后的语句代码

生成器函数同样实现了与普通函数同样的功能,它们有如下区别:

1.生成器代码更简洁。(代码少,结构更为简洁)
2.生成器内存占用极少。(与普通函数相比,普通函数面临严重的内存问题)
3.运行方式不同。(普通函数是顺序执行的,直到遇到return语句就返回,而生成器函数则是遇到yield语句返回,再次执行时,从上次离开的地方继续执行。)

6.3.3. 生成器和协程

示例

#!/usr/bin/env python
# -*- coding:utf8 -*-

import time

cake = "🍰"


# 一个简单的生产者与消费者编程模型

def consumer():
    print("等待接收处理任务.......")
    while True:
        data = (yield)
        print("收到 {0},开始品尝 【{1}】:".format(data, cake))
        time.sleep(0.5)
        print("蛋糕被吃完了........哈哈哈哈哈嗝")
        print()


def producer():
    c = consumer()
    c.__next__()
    print("模拟生产者模型")
    for i in range(1, 5):
        time.sleep(1)
        print("开始制作第{0}个蛋糕, 【{1}】【{2}】做好了.....".format(i, cake, i))
        c.send("蛋糕:【%s】" % i)


if __name__ == '__main__':
    producer()

输出信息:

等待接收处理任务.......
模拟生产者模型
开始制作第1个蛋糕, 【🍰】【1】做好了.....
收到 蛋糕:【1】,开始品尝 【🍰】:
蛋糕被吃完了........哈哈哈哈哈嗝

开始制作第2个蛋糕, 【🍰】【2】做好了.....
收到 蛋糕:【2】,开始品尝 【🍰】:
蛋糕被吃完了........哈哈哈哈哈嗝

开始制作第3个蛋糕, 【🍰】【3】做好了.....
收到 蛋糕:【3】,开始品尝 【🍰】:
蛋糕被吃完了........哈哈哈哈哈嗝

开始制作第4个蛋糕, 【🍰】【4】做好了.....
收到 蛋糕:【4】,开始品尝 【🍰】:
蛋糕被吃完了........哈哈哈哈哈嗝

6.3.4. 尽量使用生成器代替列表

##不推荐
def my_range(n):
  i = 0
  result = []
  while i < n:
    result.append(fn(i))
    i += 1
  return result # 返回列表
##推荐
def my_range(n):
  i = 0
  result = []
  while i < n:
    yield fn(i) # 使用生成器代替列表
    i += 1
*尽量用生成器代替列表,除非必须用到列表特有的函数。