你还在为数据生成逻辑和数据的消费逻辑难以分离而苦恼嘛?你还在为生成大量数据而担心内存不足嘛?来用python吧 —— 松言松语

生成器

从迭代器(Iterator)中循环遍历取出元素来处理,叫做迭代。在python中,定义了__next__方法的类,就是一个迭代器。

生成器,是数据的生产者。先说下使用到生成器的典型场景,比如:

  1. 复制文件的时候 -> 边读边写
  2. 获取1-10000里面所有的质数 -> 多次判断是否是质数

生成器的场景特征

上述场景的特征是:

这些场景的特点是:

  • 重复计算。比如循环、轮询;
  • 占用较多的空间、空间。生成大量的计算值结果集一并返回;
  • 需要用到计算出来的值。用这些值做一些具体业务处理;

生成器的处理流程

通过一个例子来看一下怎么使用:

读取文件并输出文件内容

def generator(filepath):
    block_size = 1024
    with open(filepath, 'r', encoding="utf-8") as f:
        while True:
            block = f.read(block_size)
            if block:
                # 1. 注意这里有一个yield关键字,表示返回这个变量
                yield block
            else:
                break


# 2. 调用generator,返回的是一个迭代器
it = generator("C:\\Users\\广西北部湾银行\\Desktop\\会计分录.txt")

# 3. 遍历迭代器,拿到generator中 yield(简单理解,就是’生成‘)的变量
for block in it:
    print(block)

我们先定义了一个generator函数,用来读取文件。在generator函数中yield(返回)一个叫做block的变量。

一旦函数中存在yield关键字,调用该函数的时候,便不会返回函数内的return值,而是返回一个可以迭代器it

梳理一下执行的流程:

  1. 调用生成器generator,返回一个迭代器,for自动调用内置函数next()进行迭代操作;
  2. 函数执行到关键字yield的时候会先暂停,先将yield的变量返回给调用next()的一方;
  3. 调用方处理这个值后,控制权再回到生成器中yield关键字的位置继续往下执行。如此重复。

需要知道的其他细节:

  1. 如果不用for循环的话,可以手动调用内置函数next()来获取迭代器的下一个值;

  2. yield关键字在生成器中的作用是:返回一个值给调用next()的一方,所以一般写法就是 yield变量

  3. 当数据都yield完了以后(如循环结束,不再有yield可以暂停),会报一个StopIteration的异常,需要手动处理。若调用方使用for来遍历,它已经自动帮我们捕获这个异常并停止调用next(),故无须额外处理。

生成器的优势

  • 快,且减少内存压力,不用一次性生成所有的计算值一起返回;

  • 值的处理逻辑和值的生成逻辑分离;

yield的常见写法

写法描述
result = yield 变量常见于协程1. 先变量返回给生产者(常见于需要返回协程结果的场景);
2. 暂停代码,等待外部传入一个值;
3. 外部传入值后,将其赋值给result
result = yield常见于协程和上一种写法一样,相当于yield回去一个None;
yield 变量常见于生成器只负责返回(生成)一个值

多个yield & yield from

在一个生成器中可以存在多个yield关键字。

逻辑依然不变:代码只要碰到yield就返回,重新拿到控制权后代码继续往下走。

def create():
    yield "from create"


def generator():
    yield 1
    yield 2
    yield 3
    print("----------")

    yield from [11, 22]
    print("----------")

    yield from range(3)
    print("----------")

    yield from create()


for i in generator():
    print(i)

输出结果为:

1
2
----------
11
22
----------
0
1
----------
from create

这里面还需要注意yield from关键字,它表示从一个集合、列表、或者从另外一个生成器中返回值。