关于Python:如何使用列表理解将元组的元组转换为一维列表?

How do I convert a tuple of tuples to a one-dimensional list using list comprehension?

本问题已经有最佳答案,请猛点这里访问。

我有一个元组-例如:

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

我想把它转换成一个平面的一维的所有元素的列表,顺序如下:

1
[1, 2, 3, 4, 5]

我一直试图通过列表理解来完成这项工作。但我似乎不明白。我可以通过一个for-each循环来完成它:

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

但我觉得必须有一种方法来理解列表。

一个简单的[list(tuple) for tuple in tupleOfTuples]只提供一个列表,而不是单个元素。我想我可以通过使用解包操作符来构建这个,然后解包列表,如下所示:

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

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

…但那不起作用。有什么想法吗?还是我应该坚持这个循环?


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

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

为了证明效率:

1
2
3
4
5
6
7
>>> 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作为变量名,它隐藏了内置的。


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

1
2
3
4
5
>>> 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理解或者chain.from_iterable来防止sum的二次行为。

微观基准:

  • Python 2.6

    • 短元组的长元组

      1
      2
      3
      4
      5
      6
      7
      8
      $ 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
    • 长元组的短元组

      1
      2
      3
      4
      5
      6
      7
      8
      $ 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

    • 短元组的长元组

      1
      2
      3
      4
      5
      6
      7
      8
      $ 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
    • 长元组的短元组

      1
      2
      3
      4
      5
      6
      7
      8
      $ 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))更快。


这些答案中的大多数只适用于单一水平的压平。要获得更全面的解决方案,请尝试以下方法(http://rightfootin.blogspot.com/2006/09/more on python flatten.html):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)


你把这些元组链接在一起:

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

如果你熟悉itertools,并且没有明确的list,你的结果也应该是生成器形式的,那么应该是可读的。


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

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

 > [1,2,3,4,5]


对于多级可读代码:

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

我不能把它放在一行中(即使到目前为止,它仍然是可读的)


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

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