Этот вопрос уже имеет ответ здесь:
У меня есть кортеж кортежей - например:
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]
... но это не работало. Какие-либо идеи? Или я должен просто придерживаться цикла?
обычно это называется сглаживанием вложенной структуры.
>>> 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
в качестве имени переменной, это встроенная тень.
Просто используйте 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))
работает быстрее, если внешний кортеж длинный. Вы объединяете кортежи в цепочку:
from itertools import chain
print list(chain(*listOfTuples))
Должно быть хорошо читаемым, если вы знакомы с itertools
и без явного списка
у вас даже есть результат в виде генератора.
Большинство из этих ответов будут работать только для одного уровня уплощения. Для более комплексного решения попробуйте следующее (из 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)
Мне нравится использовать "reduce" в этой ситуации (для этого и создано сокращение!)
lot = ((1, 2), (3, 4), (5,))
print list(reduce(lambda t1, t2: t1 + t2, lot))
> [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
>>> tupleOfTuples = ((1, 2), (3, 4), (5,))
>>> from itertools import chain
>>> [x for x in chain.from_iterable(tupleOfTuples)]
[1, 2, 3, 4, 5]