use*_*974 30 python performance list-comprehension list
假设我有一个列表:
l=['a','b','c']
及其后缀列表:
l2 = ['a_1', 'b_1', 'c_1']
我希望所需的输出为:
out_l = ['a','a_1','b','b_2','c','c_3']
结果是上面两个列表的交错版本.
我可以编写常规for循环来完成这项工作,但我想知道是否有更多Pythonic方式(例如,使用list comprehension或lambda)来完成它.
我尝试过这样的事情:
list(map(lambda x: x[1]+'_'+str(x[0]+1), enumerate(a)))
# this only returns ['a_1', 'b_2', 'c_3']
此外,对于一般情况需要做出哪些改变,即对于2个或更多列表,其中l2不一定是衍生物l?
cs9*_*s95 67
yield您可以使用生成器来获得优雅的解决方案.在每次迭代时,对原始元素产生两次 -once,对具有添加后缀的元素产生一次.
发电机需要耗尽; 这可以通过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的解决方案.使用构建对zip,然后使用它来展平它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 被广泛认为是pythonic列表展平方法.
推广
 
这是最简单的推广解决方案,我怀疑当N很大时,多个列表的效率最高.
list_of_lists = [l1, l2, ...]
out_l = list(chain.from_iterable(zip(*list_of_lists)))
让我们看看两个列表的简单情况(一个带有后缀的列表)的一些性能测试.一般情况不会被测试,因为结果因数据而异.
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
    ]
def transform(l):
    for i, x in enumerate(l, 1):
        yield x
        yield f'{x}_{i}'  # {}_{}'.format(x, i)
System-Mac OS X High Sierra-2.4 GHz Intel Core i7 
Python-3.6.0 
IPython-6.2.1    
你可以像这样使用列表理解:
l=['a','b','c']
new_l = [i for b in [[a, '{}_{}'.format(a, i)] for i, a in enumerate(l, start=1)] for i in b]
输出:
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']
可选,更短的方法:
[j for i, a in enumerate(l, 1) for j in [a, '{}_{}'.format(a, i)]]
你可以使用zip:
[val for pair in zip(l, [f'{k}_{j+1}' for j, k in enumerate(l)]) for val in pair]
输出:
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']