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

求itertools.product真实实现:自定义版本内存占用过高问题

itertools.product的真实实现为啥不占内存?

先说说官方文档示例为啥坑人

你从官方文档复制的示例代码,说白了就是先把所有笛卡尔积结果全生成出来存成列表,再挨个yield。比如常见的示例大概是这样:

def product(*args, repeat=1):
    pools = [tuple(pool) for pool in args] * repeat
    result = [[]]
    for pool in pools:
        result = [x+[y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)

这种写法里,result会越变越大,比如repeat=30的话,直接就内存爆炸了——毕竟2^30个元素根本存不下。

官方itertools.product的真实套路

标准库的itertools.product是用C写的,核心逻辑是靠追踪状态+懒加载,每次只生成当前需要的元素,根本不存全量结果。用Python模拟的话,大概是这个意思:

def efficient_product(*args, repeat=1):
    pools = [tuple(pool) for pool in args] * repeat
    # 用数组记录每个输入序列当前用到的索引,初始全是0
    indices = [0] * len(pools)
    # 先返回第一个元素
    yield tuple(pool[i] for pool, i in zip(pools, indices))
    
    while True:
        # 从后往前找第一个能递增的索引(类似数字进位)
        for i in reversed(range(len(pools))):
            indices[i] += 1
            if indices[i] < len(pools[i]):
                # 后面的索引重置为0
                for j in range(i+1, len(pools)):
                    indices[j] = 0
                yield tuple(pool[i] for pool, i in zip(pools, indices))
                break
        else:
            # 所有索引都没法递增了,结束
            return

这个写法的关键:

  • 懒加载到底:每次只生成当前要返回的元素,全程没搞大列表存结果
  • 状态追踪:用indices数组记着每个输入序列的当前位置,就像数数字一样,最后一位满了就进位,前面加1后面归0
  • 内存占用极低:不管你repeat多少,内存只占个索引数组的空间,跟结果总数没关系

针对你的需求:固定最后几个元素的优化

如果要固定最后几个元素,只需要改改索引的更新逻辑——让最后几个索引不动,只更新前面的部分就行。比如固定最后2个元素的版本:

def product_with_fixed_suffix(*args, fixed_suffix, repeat=1):
    args_list = list(args) * repeat
    # 先检查固定后缀长度合不合理
    assert len(fixed_suffix) <= len(args_list), "固定后缀长度不能超过总序列数"
    # 拆分可变部分和固定部分
    variable_pools = args_list[:-len(fixed_suffix)]
    fixed_pools = args_list[-len(fixed_suffix):]
    
    # 确认固定元素都在对应序列里
    for pool, val in zip(fixed_pools, fixed_suffix):
        assert val in pool, f"元素{val}不在对应的序列{pool}里"
    
    # 如果没有可变部分,直接返回固定后缀
    if not variable_pools:
        yield tuple(fixed_suffix)
        return
    
    indices = [0] * len(variable_pools)
    # 生成第一个结果
    yield tuple(pool[i] for pool, i in zip(variable_pools, indices)) + tuple(fixed_suffix)
    
    while True:
        # 只更新可变部分的索引
        for i in reversed(range(len(variable_pools))):
            indices[i] += 1
            if indices[i] < len(variable_pools[i]):
                for j in range(i+1, len(variable_pools)):
                    indices[j] = 0
                yield tuple(pool[i] for pool, i in zip(variable_pools, indices)) + tuple(fixed_suffix)
                break
        else:
            return

比如调用product_with_fixed_suffix(range(2), range(2), fixed_suffix=(1,), repeat=2),只会生成前面3个可变位置的组合,加上固定的最后一位1,根本不会生成全量的8个元素,内存占用特别小。


内容的提问来源于stack exchange,提问作者Filip Čermák

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 23:48:16