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

Python迭代类__next__未触发,返回生成器对象问题排查

迭代器类返回生成器而非预期numpy数组的问题解决

问题现象

自定义可迭代类VSIFile用于遍历VSI文件的ROI区域,实现了标准的__iter__(返回自身)和__next__方法,但执行遍历代码时,输出的是生成器对象而非预期的numpy数组。调试确认__next__方法从未被触发,仅__iter__方法执行。测试简单数字计数迭代器类运行正常,但当前类出现异常,示例输出:

<generator object VSIFile.__next__ at 0x000001AA3C4AFC80>

关联代码

vsi_file.py

from typing import Tuple
import javabridge
import bioformats
from tqdm import tqdm
from cv2 import resize

javabridge.start_vm(class_path=bioformats.JARS)


class VSIFile:

    def __init__(self, vsi_file: str,
                 roi_size: Tuple[int, int] = (1024, 1024),
                 target_size: Tuple[int, int] = (256, 256),
                 use_pbar: bool = True):

        self.file_path = vsi_file
        self.roi_size = roi_size
        self.target_size = target_size
        self.slide = None
        self.shape = None
        self.max_x_idx = None
        self.max_y_idx = None
        self.num_rois = None
        self.skip = [1, 2, 5, 11, 22, 45, 72]

        if use_pbar:
            self.pbar = tqdm()
        else:
            self.pbar = None

    def __enter__(self):
        self.slide = bioformats.ImageReader(self.file_path)
        self.shape = self.slide.rdr.getSizeY(), self.slide.rdr.getSizeX(), 3
        self.max_x_idx = self.shape[1] // self.roi_size[1]
        self.max_y_idx = self.shape[0] // self.roi_size[0]
        self.num_rois = self.max_x_idx * self.max_y_idx

        if self.pbar is not None:
            self.pbar.total = self.num_rois
            self.pbar.refresh()

        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.slide.close()

        if self.pbar is not None:
            self.pbar.close()

    def __del__(self):
        self.slide.close()

        if self.pbar is not None:
            self.pbar.close()

    def __iter__(self):
        return self

    def __next__(self):
        while self.idx in self.skip:
            if self.idx == self.max_x_idx * self.max_y_idx:
                if self.pbar is not None:
                    self.pbar.close()
                raise StopIteration

            self.idx += 1

            if self.pbar is not None:
                self.pbar.update(1)

        if self.idx == self.max_x_idx * self.max_y_idx:
            if self.pbar is not None:
                self.pbar.close()
            raise StopIteration

        y = (self.idx // self.max_x_idx) * self.roi_size[0]
        x = (self.idx % self.max_x_idx) * self.roi_size[1]

        roi = self.get_roi(x, y, self.roi_size[0], self.roi_size[1])
        roi = resize(roi, self.target_size) if self.target_size else roi
        yield roi

        self.idx += 1

        if self.pbar is not None:
            self.pbar.update(1)

process_vsi.py(相关片段)

with VSIFile(os.path.abspath(os.path.join(data_dir, file))) as vsi:
  for roi in vsi:
    print(roi)

问题根源与修复方案

核心问题1:__next__误用yield

迭代器的__next__方法要求直接返回单个元素,当使用yield时,该方法会变成生成器函数,调用时直接返回生成器对象,而非预期的numpy数组,这也是遍历得到生成器的原因。

核心问题2:未初始化迭代索引self.idx

代码中使用了self.idx但未在任何地方初始化,会导致运行时抛出AttributeError,需要在__enter__方法中添加初始化语句。

修复后的关键代码

  1. 在__enter__方法末尾添加索引初始化:
def __enter__(self):
    # 原有代码...
    self.idx = 0  # 添加这行初始化迭代索引
    return self
  1. 修改__next__方法,移除yield,改为直接返回roi,并调整后续逻辑:
def __next__(self):
    while self.idx in self.skip:
        if self.idx == self.max_x_idx * self.max_y_idx:
            if self.pbar is not None:
                self.pbar.close()
            raise StopIteration

        self.idx += 1

        if self.pbar is not None:
            self.pbar.update(1)

    if self.idx == self.max_x_idx * self.max_y_idx:
        if self.pbar is not None:
            self.pbar.close()
        raise StopIteration

    y = (self.idx // self.max_x_idx) * self.roi_size[0]
    x = (self.idx % self.max_x_idx) * self.roi_size[1]

    roi = self.get_roi(x, y, self.roi_size[0], self.roi_size[1])
    roi = resize(roi, self.target_size) if self.target_size else roi

    self.idx += 1
    if self.pbar is not None:
        self.pbar.update(1)
    
    return roi  # 替换yield为return

验证

修复后再次执行process_vsi.py的遍历代码,将输出预期的numpy数组,__next__方法会被正常触发,完成ROI的迭代输出。

内容的提问来源于stack exchange,提问作者Jack Avante

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 09:25:17