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

Python Generator调用next方法异常:提前全量迭代问题排查

API分页生成器page_helper的问题分析

问题场景

我编写了一个用于API分页的生成器page_helper,代码如下:

def page_helper(req, timeout=5, page=1, **kwargs):
    print(f"Page {page}", end="\r")
    try:
        response = req(params={**kwargs, "page": page})
        response = response.json()

    except Exception as e:
        status = response.status_code

        if status == "429":
            print(f"Rate limited. Waiting {timeout} seconds.")
            time.sleep(timeout)
            yield from page_helper(req, page=page, **kwargs)
        else:
            raise e

    else:
        if len(response) == kwargs["limit"]:
            yield from page_helper(req, page=page + 1, **kwargs)

        yield response

使用时希望每次获取一页数据作为批次,处理完成后再获取下一页,但调用next()或遍历生成器时,会直接完成全量迭代,先拉取所有页面才开始处理。示例调用代码:

batches = page_helper(<some_request>, limit=100)
# get insert and updates per batch
for i, batch in enumerate(batches):
    print(f"Batch {i + 1}", end="\r")
    insert_batch = []
    update_batch = []
    # ... process batch

调用next(batches)时直接执行完整迭代,而非仅返回一个批次,请问这个生成器代码存在什么问题?

问题核心原因

问题出在递归调用与yield的顺序错误:

  • 当前代码中,当判断当前页数据量等于limit(说明还有下一页)时,会先通过yield from page_helper(..., page=page+1, ...)递归拉取下一页及所有后续页面的全部数据,等所有递归的生成器都完成迭代后,才会执行yield response返回当前页的数据。
  • 这直接导致生成器启动后,会一次性递归遍历所有分页,把所有页面的数据提前拉取完毕,完全丧失了生成器“按需生成、分批返回”的特性。

此外还有两个细节错误:

  1. 异常捕获块中,try块抛出异常时response可能未定义,直接访问response.status_code会触发NameError。
  2. 状态码判断用了字符串"429",但实际status_code是整数类型,该判断永远不会生效。

修正后的代码

调整yield与递归调用的顺序,确保先返回当前页数据,再按需递归拉取下一页:

import time

def page_helper(req, timeout=5, page=1, **kwargs):
    print(f"Page {page}", end="\r")
    try:
        response = req(params={**kwargs, "page": page})
        response_data = response.json()

    except Exception as e:
        # 先确认异常对象包含响应信息,再判断状态码
        if hasattr(e, 'response') and e.response.status_code == 429:
            print(f"Rate limited. Waiting {timeout} seconds.")
            time.sleep(timeout)
            # 递归重试当前页,通过yield from返回结果
            yield from page_helper(req, timeout=timeout, page=page, **kwargs)
        else:
            raise e

    else:
        # 先返回当前页的数据,保证每次next()先拿到当前批次
        yield response_data
        # 如果当前页数据量等于limit,再递归拉取下一页
        if len(response_data) == kwargs.get("limit", 0):
            yield from page_helper(req, timeout=timeout, page=page + 1, **kwargs)

修正说明

  1. 调整执行顺序:先yield当前页数据,再判断是否需要递归拉取下一页,这样每次调用next()都会先返回当前批次,处理完成后才会触发下一页的拉取。
  2. 修复异常逻辑:通过hasattr(e, 'response')安全判断异常对象的响应属性,同时用整数429匹配状态码。
  3. 完善参数传递:递归时显式传递timeout参数,避免默认值覆盖自定义配置。

内容的提问来源于stack exchange,提问作者Jeroen Vermunt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 07:20:08