36

我有一个元组的元组 - 例如:

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]

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

4

7 回答 7

73

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

>>> 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用作变量名,它会影响内置的。

于 2010-07-08T13:54:54.140 回答
45

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]

如果您确实有很多元组,请使用列表推导chain.from_iterable防止sum.


微基准:

  • 蟒蛇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
      
  • 蟒蛇 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))如果外部元组很长,则速度更快。
于 2010-07-08T15:55:56.330 回答
13

您将元组链接在一起:

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

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

于 2010-07-08T14:07:16.240 回答
9

我喜欢在这种情况下使用“减少”(这就是减少的目的!)

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

 > [1,2,3,4,5]
于 2010-07-08T13:58:38.753 回答
9

这些答案中的大多数仅适用于单一级别的展平。如需更全面的解决方案,请尝试以下方法(来自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)
于 2010-07-08T14:15:17.360 回答
4

使用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]
于 2010-07-08T14:07:28.817 回答
4

对于多级且可读的代码:

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

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

于 2010-07-08T15:52:27.580 回答