Подтвердить что ты не робот

Понимание для сглаживания последовательности последовательностей?

Если у меня есть последовательность последовательностей (возможно, список кортежей), я могу использовать itertools.chain(), чтобы сгладить ее. Но иногда мне кажется, что я скорее напишу это как понимание. Я просто не могу понять, как это сделать. Здесь очень истолкованный случай:

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

>>> from itertools import chain
>>> seq = '012345'
>>> swapped_pairs = zip(seq[1::2], seq[::2])
>>> swapped_pairs
[('1', '0'), ('3', '2'), ('5', '4')]
>>> "".join(chain(*swapped_pairs))
'103254'

Я использую zip на четном и нечетном участках последовательности для замены пар. Но в итоге я получаю список кортежей, которые теперь нужно сгладить. Поэтому я использую chain(). Есть ли способ выразить это с пониманием вместо этого?

Если вы хотите опубликовать собственное решение основной проблемы обмена элементами пары, продолжайте, я буду голосовать за то, что учит меня чему-то новому. Но я только отмечу как принятый ответ, который нацелен на мой вопрос, даже если ответ "Нет, вы не можете".

4b9b3361

Ответ 1

С пониманием? Ну...

>>> seq = '012345'
>>> swapped_pairs = zip(seq[1::2], seq[::2])
>>> ''.join(item for pair in swapped_pairs for item in pair)
'103254'

Ответ 2

Самый быстрый, который я нашел, - начать с пустого массива и расширить его:

In [1]: a = [['abc', 'def'], ['ghi'],['xzy']]

In [2]: result = []

In [3]: extend = result.extend

In [4]: for l in a:
   ...:     extend(l)
   ...: 

In [5]: result
Out[5]: ['abc', 'def', 'ghi', 'xzy']

Это более чем в два раза быстрее, например, в примере Alex Martelli: Создание плоского списка из списка списков в Python

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 86.3 usec per loop

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99'  'b = []' 'extend = b.extend' 'for sub in l:' '    extend(sub)'
10000 loops, best of 3: 36.6 usec per loop

Я придумал это, потому что у меня была догадка, что за кулисами расширение будет выделять нужный объем памяти для этого списка и, вероятно, использует некоторый низкоуровневый код для перемещения элементов. Я понятия не имею, правда, но кто заботится, это быстрее.

Кстати, это только линейное ускорение:

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]'  'b = []' 'extend = b.extend' 'for sub in l:' '    extend(sub)'
1000000 loops, best of 3: 0.844 usec per loop

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]' '[item for sublist in l for item in sublist]'
1000000 loops, best of 3: 1.56 usec per loop

Вы также можете использовать map(results.extend, a), но это медленнее, поскольку он создает собственный список Nones.

Это также дает вам некоторые преимущества использования функционального программирования. т.е.

  • вы можете расширить существующий список вместо создания пустого,
  • вы все еще можете понять код с первого взгляда, минут, дней или даже месяцев спустя.

Кстати, наверное, лучше всего избегать понимания списков. Маленькие не так уж плохи, но в общем случае списки не на самом деле экономят много печатания, но часто сложнее понять и очень сложно изменить или реорганизовать (когда-либо видели понимание на уровне трех уровней?). Руководства по кодированию Google советуют против них, за исключением простых случаев. Мое мнение таково, что они полезны только в "отбрасывании" кода, то есть в коде, где автор не заботится о удобочитаемости или код, который, как известно, никогда не требует будущего обслуживания.

Сравните эти два способа написания одного и того же:

result = [item for sublist in l for item in sublist]

с этим:

result = []
for sublist in l:
    for item in sublist:
        result.append(item)

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

Ответ 3

Вы можете использовать сокращение для достижения своей цели:

In [6]: import operator
In [7]: a = [(1, 2), (2,3), (4,5)]
In [8]: reduce(operator.add, a, ())
Out[8]: (1, 2, 2, 3, 4, 5)

Это возвращает кортеж вместо списка, потому что элементы в вашем исходном списке являются кортежами, которые объединяются. Но вы можете легко создать список из этого, и метод join также принимает кортежи.

Понимание списка, кстати, не является правильным инструментом для этого. В основном понимание списка создает новый список, описывая, как должны выглядеть элементы этого списка. Вы хотите уменьшить список элементов только на одно значение.

Ответ 4

>>> a = [(1, 2), (3, 4), (5, 6)]
>>> reduce(tuple.__add__, a)
>>> (1, 2, 3, 4, 5, 6)

Или, чтобы быть агностиком по типу внутренних последовательностей (пока они все одинаковы):

>>> reduce(a[0].__class__.__add__, a)