在python中交错两个或多个列表的最佳方法?

use*_*974 30 python performance list-comprehension list

假设我有一个列表:

l=['a','b','c']
Run Code Online (Sandbox Code Playgroud)

及其后缀列表:

l2 = ['a_1', 'b_1', 'c_1']
Run Code Online (Sandbox Code Playgroud)

我希望所需的输出为:

out_l = ['a','a_1','b','b_2','c','c_3']
Run Code Online (Sandbox Code Playgroud)

结果是上面两个列表的交错版本.

我可以编写常规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']
Run Code Online (Sandbox Code Playgroud)

此外,对于一般情况需要做出哪些改变,即对于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)
Run Code Online (Sandbox Code Playgroud)

您也可以使用yield from生成器委派的语法重写它:

def transform(l):
    for i, x in enumerate(l, 1):
        yield from (x, f'{x}_{i}') # (x, {}_{}'.format(x, i))
Run Code Online (Sandbox Code Playgroud)

out_l = list(transform(l))
print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']
Run Code Online (Sandbox Code Playgroud)

如果您使用的是早于python-3.6的版本,请替换f'{x}_{i}''{}_{}'.format(x, i).

概括
考虑一般情况,您有N个表格列表:

l1 = [v11, v12, ...]
l2 = [v21, v22, ...]
l3 = [v31, v32, ...]
...
Run Code Online (Sandbox Code Playgroud)

你想要交错.这些列表不一定是相互派生的.

要使用这N个列表处理交叉操作,您需要迭代对:

def transformN(*args):
    for vals in zip(*args):
        yield from vals

out_l = transformN(l1, l2, l3, ...)
Run Code Online (Sandbox Code Playgroud)

切片 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) ...]
Run Code Online (Sandbox Code Playgroud)

print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']
Run Code Online (Sandbox Code Playgroud)

从我的时间(下图)开始,这一直是最快的.

概括
为了处理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
Run Code Online (Sandbox Code Playgroud)

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)]))) 
Run Code Online (Sandbox Code Playgroud)

print(out_l)
['a', 'a_1', 'b', 'b_2', 'c', 'c_3']
Run Code Online (Sandbox Code Playgroud)

iterools.chain 被广泛认为是pythonic列表展平方法.

推广
这是最简单的推广解决方案,我怀疑当N很大时,多个列表的效率最高.

list_of_lists = [l1, l2, ...]
out_l = list(chain.from_iterable(zip(*list_of_lists)))
Run Code Online (Sandbox Code Playgroud)

性能

让我们看看两个列表的简单情况(一个带有后缀的列表)的一些性能测试.一般情况不会被测试,因为结果因数据而异.

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
    ]
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

功能

def transform(l):
    for i, x in enumerate(l, 1):
        yield x
        yield f'{x}_{i}'  # {}_{}'.format(x, i)
Run Code Online (Sandbox Code Playgroud)

软件

System-Mac OS X High Sierra-2.4 GHz Intel Core i7
Python-3.6.0
IPython-6.2.1

  • 从可读性,简单性和维护性的角度来看,我建议使用`yield`,因为这不太可能是一个主要的瓶颈.(可能没有足够高的数据量,可能不是一个性能关键的应用程序.)生成器非常*直接理解.如果结果是问题,OP可以返回并进行优化.+1 (4认同)
  • @ user1717828我很高兴你从中学到了一些东西!它们被称为f-strings,是为python-3.6 +引入的.请查看[文档的这一部分](https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals)以获取更多信息.快乐学习! (2认同)

Aja*_*234 6

你可以像这样使用列表理解:

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]
Run Code Online (Sandbox Code Playgroud)

输出:

['a', 'a_1', 'b', 'b_2', 'c', 'c_3']
Run Code Online (Sandbox Code Playgroud)

可选,更短的方法:

[j for i, a in enumerate(l, 1) for j in [a, '{}_{}'.format(a, i)]]
Run Code Online (Sandbox Code Playgroud)


use*_*203 5

你可以使用zip:

[val for pair in zip(l, [f'{k}_{j+1}' for j, k in enumerate(l)]) for val in pair]
Run Code Online (Sandbox Code Playgroud)

输出:

['a', 'a_1', 'b', 'b_2', 'c', 'c_3']
Run Code Online (Sandbox Code Playgroud)

  • 如果你看一下时间,这比使用列表理解要快.快多了. (3认同)