Как я преобразовываю кортеж кортежей к одномерному списку с помощью понимания списка? [дубликат]

Этот вопрос уже имеет ответ здесь:

У меня есть кортеж кортежей - например:

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

Я хочу преобразовать это в плоский, одномерный список всех элементов в порядке:

[1, 2, 3, 4, 5]

Я пытался выполнить это с пониманием списка. Но я, может казаться, не понимаю это. Я смог выполнить его с циклом foreach:

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]

... но это не работало. Какие-либо идеи? Или я должен просто придерживаться цикла?

30
задан froadie 8 July 2010 в 13:58
поделиться

7 ответов

обычно это называется сглаживанием вложенной структуры.

>>> 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 в качестве имени переменной, это встроенная тень.

62
ответ дан 27 November 2019 в 22:57
поделиться

Просто используйте 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 , чтобы предотвратить квадратичное поведение суммы .


Микро-тесты:

  • Python 2.6

    • Длинный кортеж из коротких кортежей

       $ python2.6 -m timeit -s 'tot = ((1, 2),) * 500' '[элемент для tupl in tot для элемента в tupl] '
      10000 циклов, лучшее из 3: 134 мксек на цикл
      $ python2.6 -m timeit -s 'tot = ((1, 2),) * 500' 'список (сумма (tot, ()))'
      1000 петель, лучшее из 3: 1,1 мс на петлю
      $ python2.6 -m timeit -s 'tot = ((1, 2),) * 500; из цепочки импорта itertools; ci = chain.from_iterable '' список (ci (tot)) '
      10000 циклов, лучшее из 3: 60,1 мксек на цикл
      $ python2.6 -m timeit -s 'tot = ((1, 2),) * 500; из цепочки импорта itertools '' список (цепочка (* tot)) '
      10000 циклов, лучшее из 3: 64,8 мксек на цикл
      
    • Короткий кортеж из длинных кортежей

       $ python2.6 -m timeit -s 'tot = ((1,) * 500, (2,) * 500)' '[элемент для tupl в tot для элемента в tupl ] '
      10000 циклов, лучшее из 3: 65,6 мксек на цикл
      $ python2.6 -m timeit -s 'tot = ((1,) * 500, (2,) * 500)' 'список (сумма (tot, ()))'
      100000 циклов, лучшее из 3: 16,9 мксек на цикл
      $ python2.6 -m timeit -s 'tot = ((1,) * 500, (2,) * 500); из цепочки импорта itertools; ci = chain.from_iterable '' список (ci (tot)) '
      10000 циклов, лучшее из 3: 25,8 мксек на цикл
      $ python2.6 -m timeit -s 'tot = ((1,) * 500, (2,) * 500); из цепочки импорта itertools '' список (цепочка (* tot)) '
      10000 циклов, лучшее из 3: 26,5 мксек на цикл
      
  • Python 3.1

    • Длинный кортеж из коротких кортежей

       $ python3.1 -m timeit -s 'tot = ((1, 2),) * 500' '[элемент для tupl в tot для элемента в tupl]'
      10000 циклов, лучшее из 3: 121 мксек на цикл
      $ python3.1 -m timeit -s 'tot = ((1, 2),) * 500' 'список (сумма (tot, ()))'
      1000 петель, лучшее из 3: 1,09 мс на петлю
      $ python3.1 -m timeit -s 'tot = ((1, 2),) * 500; из цепочки импорта itertools; ci = chain.from_iterable '' список (ci (tot)) '
      10000 циклов, лучшее из 3: 59,5 мксек на цикл
      $ python3.1 -m timeit -s 'tot = ((1, 2),) * 500; из цепочки импорта itertools '' список (цепочка (* tot)) '
      10000 циклов, лучшее из 3: 63,2 мксек на цикл
      
    • Короткий кортеж из длинных кортежей

       $ python3.1 -m timeit -s 'tot = ((1,) * 500, (2,) * 500)' '[элемент для tupl в tot для элемента в tupl ] '
      10000 циклов, лучшее из 3: 66,1 мксек на цикл
      $ python3.1 -m timeit -s 'tot = ((1,) * 500, (2,) * 500)' 'список (сумма (tot, ()))'
      100000 циклов, лучшее из 3: 16,3 мксек на цикл
      $ python3.1 -m timeit -s 'tot = ((1,) * 500, (2,) * 500); из цепочки импорта itertools; ci = chain.from_iterable '' список (ci (tot)) '
      10000 циклов, лучшее из 3: 25,4 мксек на цикл
      $ python3.1 -m timeit -s 'tot = ((1,) * 500, (2,) * 500); из цепочки импорта itertools '' список (цепочка (* tot)) '
      10000 циклов, лучшее из 3: 25,6 мксек на цикл
      

Наблюдение:

  • сумма выполняется быстрее, если внешний кортеж короткий.
  • list (chain.from_iterable (x)) работает быстрее, если внешний кортеж длинный.
39
ответ дан 27 November 2019 в 22:57
поделиться

Вы объединяете кортежи в цепочку:

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

Должно быть хорошо читаемым, если вы знакомы с itertools и без явного списка у вас даже есть результат в виде генератора.

10
ответ дан 27 November 2019 в 22:57
поделиться

Большинство из этих ответов будут работать только для одного уровня уплощения. Для более комплексного решения попробуйте следующее (из 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)
9
ответ дан 27 November 2019 в 22:57
поделиться

Мне нравится использовать "reduce" в этой ситуации (для этого и создано сокращение!)

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

 > [1,2,3,4,5]
9
ответ дан 27 November 2019 в 22:57
поделиться

Для многоуровневого и читабельного кода:

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

Я не смог заставить это уместиться в одну строку (и остаться читабельным, даже далеко)

4
ответ дан 27 November 2019 в 22:57
поделиться

Другое решение с использованием 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]
1
ответ дан 27 November 2019 в 22:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: