【问题标题】:All combinations of a list of lists列表列表的所有组合
【发布时间】:2026-02-09 05:25:01
【问题描述】:

我基本上是在寻找Combination of List<List<int>>的python版本

给定一个列表列表,我需要一个新列表来提供列表之间所有可能的项目组合。

[[1,2,3],[4,5,6],[7,8,9,10]] -> [[1,4,7],[1,4,8],...,[3,6,10]]

列表的数量是未知的,所以我需要适用于所有情况的东西。优雅加分!

【问题讨论】:

    标签: python combinations


    【解决方案1】:

    你需要itertools.product:

    >>> import itertools
    >>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
    >>> list(itertools.product(*a))
    [(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]
    

    【讨论】:

    • 有人能解释一下*a中星号的含义吗?
    • *a 表示这些是传递给函数或方法的参数。 def fn(a,b,c): 会回复 fn(*[1,2,3]) reference
    • @mjallday,是否可以添加这些组合:(7,4,1),(8,4,1),(9,4,1),(10,4, 1),(7,5,1),(8,5,1),(9,5,1),(10,5,1) 等等?
    • @Reman 并不完全清楚你想要得到什么,但如果它是,例如,每个元组的反转,你可以使用一个包装函数,将a 作为输入,迭代@ 987654330@ 和 yields 都是由 itertools 生成的元组和反向版本(例如,创建一个列表,reverse() 它并转换回元组)。最好提出一个新问题。
    • 如果您熟悉 javascript [*a] 将与 js 扩展运算符 [...a] 相同。 dicts 也是如此
    【解决方案2】:

    这个任务直接递归没有错,不需要外部依赖,如果你需要一个可以处理字符串的版本,这可能符合你的需要:

    combinations = []
    
    def combine(terms, accum):
        last = (len(terms) == 1)
        n = len(terms[0])
        for i in range(n):
            item = accum + terms[0][i]
            if last:
                combinations.append(item)
            else:
                combine(terms[1:], item)
    
    
    >>> a = [['ab','cd','ef'],['12','34','56']]
    >>> combine(a, '')
    >>> print(combinations)
    ['ab12', 'ab34', 'ab56', 'cd12', 'cd34', 'cd56', 'ef12', 'ef34', 'ef56']
    

    【讨论】:

      【解决方案3】:
      listOLists = [[1,2,3],[4,5,6],[7,8,9,10]]
      for list in itertools.product(*listOLists):
        print list;
      

      我希望你发现它和我第一次遇到它时一样优雅。

      【讨论】:

      • 那个分号是怎么回事? :)
      • 习惯的力量。我喜欢 Python 让你放一个分号,只是为了帮助我们这些 C/Java 程序员。但很清楚;当您执行诸如 print("foo");; 之类的操作时,它并不是真正的语句终止符;这在 C 或 Java 中是完全合法的(尽管毫无意义),但在 Python 中被禁止。
      【解决方案4】:

      这主要模仿使用itertools.productAnswer by Jarret Hardie 等解决方案,但有以下区别:

      • 这会将参数传递给itertools.product 内联,而不是通过变量a - 所以内联参数不需要*args 语法
      • 如果你的mypy type-linter 像我的一样,并且你可以让你的代码以其他方式“工作”使用带有内联product 参数的*args 语法(如product(*[[1,2,3],[4,5,6],[7,8,9,10]])),mypy 可能仍然会失败(类似error: No overload variant of "product" matches argument type "List[object]")
      • 所以mypy 的解决方案是不使用*args 语法,如下所示:
          >>> import itertools
          >>> list(itertools.product([1,2,3],[4,5,6],[7,8,9,10]))
          [(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]
      

      【讨论】:

        【解决方案5】:

        Numpy 可以做到:

         >>> import numpy
         >>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
         >>> [list(x) for x in numpy.array(numpy.meshgrid(*a)).T.reshape(-1,len(a))]
        [[ 1, 4, 7], [1, 5, 7], [1, 6, 7], ....]
        

        【讨论】:

        • 有人能解释一下吗?
        • 如果 a 中的东西是数组,这不起作用。 a=[[[array A], [array B], [array C]], [...]
        • 这感觉就像在问“我如何烧水”,而不是说“使用水壶”,而是“降低水周围的大气压力”。两者都有效!
        【解决方案6】:

        可以为此使用基础 python。代码需要一个函数来展平列表:

        def flatten(B):    # function needed for code below;
            A = []
            for i in B:
                if type(i) == list: A.extend(i)
                else: A.append(i)
            return A
        

        然后可以运行:

        L = [[1,2,3],[4,5,6],[7,8,9,10]]
        
        outlist =[]; templist =[[]]
        for sublist in L:
            outlist = templist; templist = [[]]
            for sitem in sublist:
                for oitem in outlist:
                    newitem = [oitem]
                    if newitem == [[]]: newitem = [sitem]
                    else: newitem = [newitem[0], sitem]
                    templist.append(flatten(newitem))
        
        outlist = list(filter(lambda x: len(x)==len(L), templist))  # remove some partial lists that also creep in;
        print(outlist)
        

        输出:

        [[1, 4, 7], [2, 4, 7], [3, 4, 7], 
        [1, 5, 7], [2, 5, 7], [3, 5, 7], 
        [1, 6, 7], [2, 6, 7], [3, 6, 7], 
        [1, 4, 8], [2, 4, 8], [3, 4, 8], 
        [1, 5, 8], [2, 5, 8], [3, 5, 8], 
        [1, 6, 8], [2, 6, 8], [3, 6, 8], 
        [1, 4, 9], [2, 4, 9], [3, 4, 9], 
        [1, 5, 9], [2, 5, 9], [3, 5, 9], 
        [1, 6, 9], [2, 6, 9], [3, 6, 9], 
        [1, 4, 10], [2, 4, 10], [3, 4, 10], 
        [1, 5, 10], [2, 5, 10], [3, 5, 10], 
        [1, 6, 10], [2, 6, 10], [3, 6, 10]]
        

        【讨论】:

          【解决方案7】:

          最优雅的解决方案是在 python 2.6 中使用itertools.product

          如果您使用的不是 Python 2.6,itertools.product 的文档实际上显示了一个等效功能,可以以“手动”方式完成产品:

          def product(*args, **kwds):
              # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
              # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
              pools = map(tuple, args) * kwds.get('repeat', 1)
              result = [[]]
              for pool in pools:
                  result = [x+[y] for x in result for y in pool]
              for prod in result:
                  yield tuple(prod)
          

          【讨论】:

            【解决方案8】:
            from itertools import product 
            list_vals = [['Brand Acronym:CBIQ', 'Brand Acronym :KMEFIC'],['Brand Country:DXB','Brand Country:BH']]
            list(product(*list_vals))
            

            输出:

            [('品牌缩写:CBIQ', '品牌国家:DXB'),
            ('品牌缩写:CBIQ','品牌国家:BH'),
            ('品牌缩写:KMEFIC','品牌国家:DXB'),
            ('品牌缩写:KMEFIC','品牌国家:BH')]

            【讨论】:

            • 这个答案应该被接受,因为它是唯一一个使用内置函数的答案,同时强调它也适用于任何类型和异构类型。
            • 这个答案与多年前提供的答案有何不同?
            • 这里的参数类型是同质的,不是异质的。
            【解决方案9】:

            这个答案不如使用 itertools 干净,但这些想法可能很有用。

            从 zip() here 的构造中汲取灵感,我们可以做到以下几点。

            >>> a = iter([[1,2,3],[4,5,6],[7,8,9,10]])
            >>> sentinel = object()
            >>> result = [[]]
            >>> while True:
            >>>     l = next(a,sentinel)
            >>>     if l == sentinel:
            >>>         break
            >>>     result = [ r + [digit] for r in result for digit in l]
            >>> print(result)
            [[1, 4, 7], [1, 4, 8], [1, 4, 9], [1, 4, 10], [1, 5, 7], [1, 5, 8], [1, 5, 9], [1, 5, 10], [1, 6, 7], [1, 6, 8], [1, 6, 9], [1, 6, 10], [2, 4, 7], [2, 4, 8], [2, 4, 9], [2, 4, 10], [2, 5, 7], [2, 5, 8], [2, 5, 9], [2, 5, 10], [2, 6, 7], [2, 6, 8], [2, 6, 9], [2, 6, 10], [3, 4, 7], [3, 4, 8], [3, 4, 9], [3, 4, 10], [3, 5, 7], [3, 5, 8], [3, 5, 9], [3, 5, 10], [3, 6, 7], [3, 6, 8], [3, 6, 9], [3, 6, 10]]
            

            我们使用a 作为迭代器,以便连续获取它的下一项,而不需要先验知道有多少。当我们用完a 中的列表时,next 命令将输出sentinel(这是一个仅为进行比较而创建的对象,请参阅here 以获得一些解释),从而导致if 语句触发我们跳出循环。

            【讨论】:

            • 确实,这或多或少是 itertools.product 所做的, -- 参见 here -- 除了使用哨兵,而 itertools.product 使用 yield
            最近更新 更多