Ответ 1
yield
Вы можете использовать генератор для элегантного решения. На каждой итерации выдаете дважды - один раз с исходным элементом и один раз с элементом с добавленным суффиксом.
Генератор должен быть исчерпан; это можно сделать, прикрепив вызов list
в конце.
def transform(l):
for i, x in enumerate(l, 1):
yield x
yield f'{x}_{i}' # {}_{}'.format(x, i)
Вы также можете переписать это, используя синтаксис yield from
для делегирования генератора:
def transform(l):
for i, x in enumerate(l, 1):
yield from (x, f'{x}_{i}') # (x, {}_{}'.format(x, i))
out_l = list(transform(l))
print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']
Если вы используете версию, более раннюю, чем python-3.6, замените f'{x}_{i}'
'{}_{}'.format(x, i)
.
Обобщая
Рассмотрим общий сценарий, где у вас есть N списков в форме:
l1 = [v11, v12, ...]
l2 = [v21, v22, ...]
l3 = [v31, v32, ...]
...
Который вы хотели бы чередовать. Эти списки не обязательно являются производными друг от друга.
Для обработки операций чередования с этими N списками вам нужно будет выполнить итерации по парам:
def transformN(*args):
for vals in zip(*args):
yield from vals
out_l = transformN(l1, l2, l3, ...)
list.__setitem__
Я бы порекомендовал это с точки зрения производительности. Сначала выделите место для пустого списка, а затем назначьте элементы списка на их соответствующие позиции, используя назначение разделенного списка. l
переходит в четные индексы, а l'
(l
модифицированный) переходит в нечетные индексы.
out_l = [None] * (len(l) * 2)
out_l[::2] = l
out_l[1::2] = [f'{x}_{i}' for i, x in enumerate(l, 1)] # [{}_{}'.format(x, i) ...]
print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']
Это неизменно самый быстрый из моих таймингов (ниже).
Обобщая
Чтобы обрабатывать N списков, итеративно присваивайте срезы.
list_of_lists = [l1, l2, ...]
out_l = [None] * len(list_of_lists[0]) * len(list_of_lists)
for i, l in enumerate(list_of_lists):
out_l[i::2] = l
zip
+ chain.from_iterable
Функциональный подход, аналогичный решению @chrisz. itertools.chain
пары, используя zip
а затем itertools.chain
их, используя itertools.chain
.
from itertools import chain
# [{}_{}'.format(x, i) ...]
out_l = list(chain.from_iterable(zip(l, [f'{x}_{i}' for i, x in enumerate(l, 1)])))
print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']
iterools.chain
широко считается подходом к уплощению списка.
Обобщая
Это самое простое решение для обобщения, и я подозреваю, что наиболее эффективно для нескольких списков, когда N велико.
list_of_lists = [l1, l2, ...]
out_l = list(chain.from_iterable(zip(*list_of_lists)))
Спектакль
Давайте взглянем на некоторые perf-тесты для простого случая двух списков (один список с суффиксом). Общие случаи не будут проверяться, так как результаты широко варьируются в зависимости от данных.
Код для сравнения, для справки.
функции
def cs1(l):
def _cs1(l):
for i, x in enumerate(l, 1):
yield x
yield f'{x}_{i}'
return list(_cs1(l))
def cs2(l):
out_l = [None] * (len(l) * 2)
out_l[::2] = l
out_l[1::2] = [f'{x}_{i}' for i, x in enumerate(l, 1)]
return out_l
def cs3(l):
return list(chain.from_iterable(
zip(l, [f'{x}_{i}' for i, x in enumerate(l, 1)])))
def ajax(l):
return [
i for b in [[a, '{}_{}'.format(a, i)]
for i, a in enumerate(l, start=1)]
for i in b
]
def ajax_cs0(l):
# suggested improvement to ajax solution
return [j for i, a in enumerate(l, 1) for j in [a, '{}_{}'.format(a, i)]]
def chrisz(l):
return [
val
for pair in zip(l, [f'{k}_{j+1}' for j, k in enumerate(l)])
for val in pair
]