【问题标题】:How do I convert a tuple of tuples to a one-dimensional list using list comprehension? [duplicate]如何使用列表推导将元组的元组转换为一维列表? [复制]
【发布时间】:2011-03-13 08:47:37
【问题描述】:

我有一个元组 - 例如:

tupleOfTuples = ((1, 2), (3, 4), (5,))

我想将其转换为按顺序排列的所有元素的平面一维列表:

[1, 2, 3, 4, 5]

我一直在尝试通过列表理解来实现这一点。但我似乎无法弄清楚。我能够通过 for-each 循环来完成它:

myList = []
for tuple in tupleOfTuples:
   myList = myList + list(tuple)

但我觉得必须有一种方法可以通过列表理解来做到这一点。

一个简单的[list(tuple) for tuple in tupleOfTuples] 只是给你一个列表,而不是单个元素。我想我也许可以通过使用解包运算符来解包列表,如下所示:

[*list(tuple) for tuple in tupleOfTuples]

[*(list(tuple)) for tuple in tupleOfTuples]

...但这没有用。有任何想法吗?还是我应该坚持循环?

【问题讨论】:

    标签: python tuples list-comprehension iterable-unpacking


    【解决方案1】:

    它通常被称为扁平化嵌套结构。

    >>> tupleOfTuples = ((1, 2), (3, 4), (5,))
    >>> [element for tupl in tupleOfTuples for element in tupl]
    [1, 2, 3, 4, 5]
    

    只是为了展示效率:

    >>> import timeit
    >>> it = lambda: list(chain(*tupleOfTuples))
    >>> timeit.timeit(it)
    2.1475738355700913
    >>> lc = lambda: [element for tupl in tupleOfTuples for element in tupl]
    >>> timeit.timeit(lc)
    1.5745135182887857
    

    ETA:请不要使用tuple 作为变量名,它会影响内置。

    【讨论】:

    • 谢谢...你介意给 i 和 j 更有意义的名字,这样我就可以更容易地遵循这个逻辑吗?
    • @SilentGhost - 谢谢!这是否被广泛接受?乍一看,我发现它比更长的循环更难理解......但如果程序员认识到这种模式,那么我会使用它
    • @froadie:这是一种扁平化浅表的惯用方式。
    • 我想知道它如何查找更长的列表,即tupleOfTuples=tuple(zip(range(0,100,2), range(1,100,2)))——在这种情况下,chain 比 LC 稍快(而 Fabian 的 chain.from_iterable 是最快的)
    • 努力了解它的工作原理。您可以推荐任何资源并逐步解释它吗?
    【解决方案2】:

    如果您没有很多元组,只需使用 sum

    >>> tupleOfTuples = ((1, 2), (3, 4), (5,))
    >>> sum(tupleOfTuples, ())
    (1, 2, 3, 4, 5)
    >>> list(sum(tupleOfTuples, ())) # if you really need a list
    [1, 2, 3, 4, 5]
    

    如果您确实有很多元组,请使用list comprehensionchain.from_iterable 来防止sum 的二次行为。


    微基准测试:

    • Python 2.6

      • 短元组的长元组

        $ python2.6 -m timeit -s 'tot = ((1, 2), )*500' '[element for tupl in tot for element in tupl]'
        10000 loops, best of 3: 134 usec per loop
        $ python2.6 -m timeit -s 'tot = ((1, 2), )*500' 'list(sum(tot, ()))'
        1000 loops, best of 3: 1.1 msec per loop
        $ python2.6 -m timeit -s 'tot = ((1, 2), )*500; from itertools import chain; ci = chain.from_iterable' 'list(ci(tot))'
        10000 loops, best of 3: 60.1 usec per loop
        $ python2.6 -m timeit -s 'tot = ((1, 2), )*500; from itertools import chain' 'list(chain(*tot))'
        10000 loops, best of 3: 64.8 usec per loop
        
      • 长元组的短元组

        $ python2.6 -m timeit -s 'tot = ((1, )*500, (2, )*500)' '[element for tupl in tot for element in tupl]'
        10000 loops, best of 3: 65.6 usec per loop
        $ python2.6 -m timeit -s 'tot = ((1, )*500, (2, )*500)' 'list(sum(tot, ()))'
        100000 loops, best of 3: 16.9 usec per loop
        $ python2.6 -m timeit -s 'tot = ((1, )*500, (2, )*500); from itertools import chain; ci = chain.from_iterable' 'list(ci(tot))'
        10000 loops, best of 3: 25.8 usec per loop
        $ python2.6 -m timeit -s 'tot = ((1, )*500, (2, )*500); from itertools import chain' 'list(chain(*tot))'
        10000 loops, best of 3: 26.5 usec per loop
        
    • Python 3.1

      • 短元组的长元组

        $ python3.1 -m timeit -s 'tot = ((1, 2), )*500' '[element for tupl in tot for element in tupl]'
        10000 loops, best of 3: 121 usec per loop
        $ python3.1 -m timeit -s 'tot = ((1, 2), )*500' 'list(sum(tot, ()))'
        1000 loops, best of 3: 1.09 msec per loop
        $ python3.1 -m timeit -s 'tot = ((1, 2), )*500; from itertools import chain; ci = chain.from_iterable' 'list(ci(tot))'
        10000 loops, best of 3: 59.5 usec per loop
        $ python3.1 -m timeit -s 'tot = ((1, 2), )*500; from itertools import chain' 'list(chain(*tot))'
        10000 loops, best of 3: 63.2 usec per loop
        
      • 长元组的短元组

        $ python3.1 -m timeit -s 'tot = ((1, )*500, (2, )*500)' '[element for tupl in tot for element in tupl]'
        10000 loops, best of 3: 66.1 usec per loop
        $ python3.1 -m timeit -s 'tot = ((1, )*500, (2, )*500)' 'list(sum(tot, ()))'
        100000 loops, best of 3: 16.3 usec per loop
        $ python3.1 -m timeit -s 'tot = ((1, )*500, (2, )*500); from itertools import chain; ci = chain.from_iterable' 'list(ci(tot))'
        10000 loops, best of 3: 25.4 usec per loop
        $ python3.1 -m timeit -s 'tot = ((1, )*500, (2, )*500); from itertools import chain' 'list(chain(*tot))'
        10000 loops, best of 3: 25.6 usec per loop
        

    观察:

    • 如果外部元组很短,sum 会更快。
    • 如果外部元组很长,list(chain.from_iterable(x)) 会更快。

    【讨论】:

    • 哇...没想到。对于其他也感到惊讶的人,请注意如果 b = ((1, 2,3), (4, 5), (5,6,7,8))sum(b)(1, 2, 3, 4, 5, 5, 6, 7, 8).... 如果 c = [(1, 2,3), (4, 5), (5,6,7,8)]sum(c)(1, 2, 3, 4, 5, 5, 6, 7, 8)。我认为这非常有用!
    • 这仅适用于具有可求和数据类型的元组。
    • 如果tupleOfTuples 不包含所有元组,这将不起作用 :)
    【解决方案3】:

    您将元组链接在一起:

    from itertools import chain
    print list(chain(*listOfTuples))
    

    如果您熟悉 itertools,则应该非常易读,如果没有明确的 list,您甚至可以生成生成器形式的结果。

    【讨论】:

      【解决方案4】:

      我喜欢在这种情况下使用“reduce”(这就是 reduce 的用途!)

      lot = ((1, 2), (3, 4), (5,))
      print list(reduce(lambda t1, t2: t1 + t2, lot))
      
       > [1,2,3,4,5]
      

      【讨论】:

      • 在 Alex 以他的仇恨换地图/减少愤怒投票反对你之前
      • 有兴趣了解这与对大型列表的 python 列表理解的比较(效率)
      【解决方案5】:

      这些答案中的大多数仅适用于单一级别的展平。如需更全面的解决方案,请试试这个(来自http://rightfootin.blogspot.com/2006/09/more-on-python-flatten.html):

      def flatten(l, ltypes=(list, tuple)):
          ltype = type(l)
          l = list(l)
          i = 0
          while i < len(l):
              while isinstance(l[i], ltypes):
                  if not l[i]:
                      l.pop(i)
                      i -= 1
                      break
                  else:
                      l[i:i + 1] = l[i]
              i += 1
          return ltype(l)
      

      【讨论】:

      • 帮助我展平了 Tree 数据结构,而其他解决方案还不够深入!
      • 我喜欢这个解决方案 - 我更喜欢通用解决方案而不是特定解决方案。
      【解决方案6】:

      使用itertools.chain的另一种解决方案

      >>> tupleOfTuples = ((1, 2), (3, 4), (5,))
      >>> from itertools import chain
      >>> [x for x in chain.from_iterable(tupleOfTuples)]
      [1, 2, 3, 4, 5]
      

      【讨论】:

        【解决方案7】:

        对于多级且可读的代码:

        def flatten(bla):
            output = []
            for item in bla:
                output += flatten(item) if hasattr (item, "__iter__") or hasattr (item, "__len__") else [item]
            return output
        

        我无法将它放在一行中(并且仍然可读,即使到目前为止)

        【讨论】:

        • 有些代码不应该放在一行中。
        • 对不同深度的列表很有帮助。
        • isinstance(item, tuple) 而不是 hasattr (item, "iter") 或 hasattr (item, "len") 会成功更短
        • 是的,但它只适用于元组 - 上面的表达式适用于列表中的任何可迭代元素 - 包括生成器。
        猜你喜欢
        • 1970-01-01
        • 2015-09-24
        • 2015-11-20
        • 1970-01-01
        • 1970-01-01
        • 2015-02-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多