如何使用列表推导将元组的元组转换为一维列表?

39 投票
7 回答
73517 浏览
提问于 2025-04-16 00:59

我有一个包含元组的元组,比如说:

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

我想把它转换成一个扁平的一维列表,里面包含所有的元素,顺序也要对:

[1, 2, 3, 4, 5]

我一直在尝试用列表推导式来实现这个目标,但我总是搞不定。其实我用循环是可以做到的:

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]

...但这样也不行。有什么想法吗?还是我就继续用循环吧?

7 个回答

13

你正在把元组连接在一起:

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

如果你对 itertools 有点了解,这段代码应该挺容易理解的。而且如果不使用明确的 list,你得到的结果还是生成器的形式。

46

如果你的元组不多,就直接用 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 的性能问题。


微基准测试:

  • 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)) 会更快。
76

这通常被称为“扁平化嵌套结构”。

>>> 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

注意: 请不要把 tuple 用作变量名,这样会覆盖掉内置的功能。

撰写回答