Python itertools.islice拆分生成器出现最后分片为空异常求解
问题根因
- 普通迭代器是单向不可回溯、一次性消耗的对象,
itertools.islice每次调用时,会从迭代器的当前已读取位置开始计数,而非从迭代器的初始起始位置计数。 - 你原代码的非列表/元组分支完全复用了随机访问序列的下标逻辑,每次计算历史偏移量
prev,调用islice(array, prev, next_),会导致每轮都会额外跳过prev个已经被读过的元素,直接提前耗空迭代器。
以你提供的测试用例拆解运行过程即可验证:
测试迭代器长度为8,拆分为3片,计算得到
floor=2, rem=2,预期每轮分别取3、3、2个元素:
- 第一轮调用
islice(iter, 0, 3):读取前3个元素[12,7,9],迭代器当前位置已经走到第4个元素(值为31)的位置- 第二轮按原有逻辑计算得到
prev=3,调用islice(iter, 3, 6):islice会先从当前位置(第4个元素)往后跳3个元素,跳过31、13、11,直接到第7个元素(值为7)的位置,再往后读最多3个,只能读到7、3两个,所以第二片输出[7,3]- 第三轮计算得到
prev=6,调用islice(iter,6,8):迭代器已经到末尾,跳6个直接超出范围,返回空列表
当分片数更多、每轮跳转的prev值更大时,会更早耗空迭代器,出现最后两个甚至更多分片为空的情况。
修复方案
非随机访问的迭代器分支不需要计算历史偏移量,每轮islice的起始位固定为0,仅控制每个分片要读取的长度即可,修复后代码如下:
import itertools def divide_list(array, n, gen_length=None): """ :param array: some iterable that you wish to divide :param n: the number of lists you would like to return :param gen_length: The length of the generator if array is a generator. Not necessary for lists and tuples. :return: a generator of the divided list Example: In: list(divide_list([1, 2, 3, 4, 5, 6, 7, 8, 9], 4)) Out: [[1, 2, 3], [4, 5], [6, 7], [8, 9]] """ if isinstance(array, (list, tuple)): floor, rem = divmod(len(array), n) items_index = (0, floor) for _ in range(n): prev, next_ = items_index[0], items_index[1] + 1 if rem > 0 else items_index[1] yield array[prev:next_] items_index = (next_, next_ + floor) rem -= 1 else: floor, rem = divmod(gen_length, n) for _ in range(n): # 每轮分片长度 = 基础长度 + 1(如果还有余数) slice_len = floor + 1 if rem > 0 else floor yield itertools.islice(array, slice_len) rem -= 1 if __name__ == '__main__': array_ = iter([12, 7, 9, 31, 13, 11, 7, 3]) print('Generator:') print('----------') for value in divide_list(array_, 3, gen_length=8): print(list(value)) print('') array_ = [12, 7, 9, 31, 13, 11, 7, 3] print('List:') print('-----') for value in divide_list(array_, 3): print(value)
修复后运行输出符合预期:
Generator: ---------- [12, 7, 9] [31, 13, 11] [7, 3] List: ----- [12, 7, 9] [31, 13, 11] [7, 3]
内容的提问来源于stack exchange,提问作者Gabe Morris
相关产品推荐
相关产品推荐

