You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python生成器中嵌套非生成器函数实现yield的优雅方案

纯生成器风格的重构方案

你需要消除生成器中重复的"添加元素-检查批量-产出-重置"逻辑,同时保留生成器的惰性特性(不提前存储所有结果),以下是几种优雅的实现方式:

方案一:封装逻辑为辅助生成器,用yield from产出结果

把重复的逻辑封装成内部辅助函数,该函数返回一个生成器(要么为空,要么包含当前批量),通过yield from批量产出结果。既消除了代码重复,又完全保留生成器的惰性:

from typing import Generator

def f() -> Generator[list[int], None, None]:
    result = []
    
    def add_and_check(value: int) -> Generator[list[int], None, None]:
        nonlocal result
        result.append(value)
        if len(result) == 2:
            yield result
            result = []
    
    # 逐个添加元素,通过yield from产出符合条件的批量
    yield from add_and_check(1)
    yield from add_and_check(2)
    yield from add_and_check(3)
    yield from add_and_check(4)

print(list(f()))  # 输出: [[1, 2], [3, 4]]

方案二:使用协程(Send方法)处理动态添加

如果你的实际场景中元素是动态产生(而非预先知道所有元素),可以用协程实现批量收集器,通过send()方法添加元素,同时产出批量结果:

from typing import Generator

def batch_collector(batch_size: int) -> Generator[list[int] | None, int, None]:
    batch = []
    while True:
        # 接收外部send的元素
        value = yield
        batch.append(value)
        if len(batch) == batch_size:
            # 产出批量结果,之后回到等待接收状态
            yield batch
            batch = []

def f() -> Generator[list[int], None, None]:
    collector = batch_collector(2)
    next(collector)  # 启动协程到第一个接收点
    
    # 逐个发送元素,并处理产出的批量
    for val in [1, 2, 3, 4]:
        collector.send(val)
        # 尝试获取批量结果(若存在)
        batch = next(collector)
        if batch:
            yield batch

print(list(f()))  # 输出: [[1, 2], [3, 4]]

方案三:面向对象封装批量逻辑

如果需要更灵活的状态管理(比如支持中途修改批量大小、处理剩余元素),可以用类封装批量生成逻辑,通过add()方法返回迭代器,再用yield from产出:

from typing import Generator, Iterator

class BatchGenerator:
    def __init__(self, batch_size: int):
        self.batch_size = batch_size
        self.current_batch = []
    
    def add(self, value: int) -> Iterator[list[int]]:
        self.current_batch.append(value)
        if len(self.current_batch) == self.batch_size:
            yield self.current_batch
            self.current_batch = []
    
    def flush(self) -> Iterator[list[int]]:
        # 处理最后剩余的不足批量的元素(若需要)
        if self.current_batch:
            yield self.current_batch

def f() -> Generator[list[int], None, None]:
    bg = BatchGenerator(2)
    yield from bg.add(1)
    yield from bg.add(2)
    yield from bg.add(3)
    yield from bg.add(4)
    # 若有剩余元素,可添加 yield from bg.flush()

print(list(f()))  # 输出: [[1, 2], [3, 4]]

方案四:利用迭代器工具简化(适用于已知元素序列)

如果你的元素是预先存在的序列,可以直接用itertools结合生成器表达式实现分组,这是最简洁的方式:

from typing import Generator
import itertools

def f() -> Generator[list[int], None, None]:
    items = [1, 2, 3, 4]
    it = iter(items)
    # 每次从迭代器取2个元素组成批量
    yield from (list(itertools.islice(it, 2)) for _ in itertools.repeat(None))

print(list(f()))  # 输出: [[1, 2], [3, 4]]

以上所有方案都遵循了生成器的惰性原则,不会提前将所有结果存储在内存中,完美贴合你的需求。

内容的提问来源于stack exchange,提问作者Vedran Šego

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 22:05:20