如何在列表理解中一次有条件地放置多个元素?

2024-10-03 21:36:29 发布

您现在位置:Python中文网/ 问答频道 /正文

我发现这个问题离我的问题很近:

Adding two items at a time in a list comprehension

但如果我需要在单人间或双人间切换,比如:

original = list(range(10))
required = [0,0,1,2,2,3,4,4,5,6,6,7,8,8,9]
attempt1 = sum([[x,x] if x%2 == 0 else [x] for x in original],[])
attempt2 = [i for x in original for i in ([x,x] if x%2 == 0 else [x])]

sum似乎是slow,列表理解很难阅读。它们都不能让我感觉简单和美好。你知道吗

有更好的方法吗?还是放弃单行道?或者说服我其中一个是不是真的很好的风格。你知道吗


Tags: inforiftimeitemselseatlist
3条回答

简单易读的解决方案如下所示:

my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
new_list = [[i] * 2 if i % 2 == 0 else i for i in my_list]
res = []
for i in new_list:
    if isinstance(i, list):
        res.extend(i)
    else:
        res.append(i)

输出:

>>> res
[0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9]

就我个人而言,只要我在理解中有更多的非琐碎的东西(例如2for和1if),我就会使用生成器函数。你知道吗

例如,在您的案例中,您可以使用(我认为它更具可读性,但这可能是主观的):

def double_evens(inp):
    for item in inp:
        if item % 2 == 0:
            yield item
        yield item

试运行:

>>> list(double_evens(range(10)))
[0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9]

请注意,这种方法甚至可以更快(它比答案中的其他解决方案快3倍,比你在我的电脑上的理解快2倍)。从this answer获取计时框架:

from itertools import chain

def coldspeed1(mylist):
    return [y for x in mylist for y in [x] * (2 - x % 2)]

def coldspeed2(mylist):
    return list(chain.from_iterable([x] * (2 - x % 2) for x in mylist))

def double_evens(inp):
    for item in inp:
        if not item % 2:
            yield item
        yield item

def mseifert(inp):
    return list(double_evens(inp))  

def ettanany(my_list):
    new_list = [[i] * 2 if i % 2 == 0 else i for i in my_list]
    res = []
    for i in new_list:
        if isinstance(i, list):
            res.extend(i)
        else:
            res.append(i)
    return res

def no1xsyzy(original):
    return [i for x in original for i in ([x,x] if x%2 == 0 else [x])]

# Timing setup
timings = {coldspeed1: [], coldspeed2: [], mseifert: [], ettanany: [], no1xsyzy: []}
sizes = [2**i for i in range(1, 20, 2)]

# Timing
for size in sizes:
    mylist = list(range(size))
    for func in timings:
        res = %timeit -o func(mylist)
        timings[func].append(res)

# Plotting
%matplotlib notebook

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(1)
ax = plt.subplot(111)

baseline = mseifert # choose one function as baseline
for func in timings:
    ax.plot(sizes, 
            [time.best / ref.best for time, ref in zip(timings[func], timings[baseline])], 
            label=str(func.__name__))
#ax.set_yscale('log')
ax.set_xscale('log')
ax.set_xlabel('size')
ax.set_ylabel('time relative to {}'.format(baseline.__name__))
ax.grid(which='both')
ax.legend()
plt.tight_layout()

enter image description here

此图绘制了与我的解决方案相比的相对时间差。请注意,x轴(大小)是对数的,而y轴(时差)不是

你在正确的轨道上。您可以通过在list comp中用*操作替换和if..else来进行简化。你知道吗

In [53]: [y for x in range(10) for y in [x] * (2 - x % 2)]
Out[53]: [0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9]

这大致可以理解为:

res = []
for x in range(10):
    if not (x + 1) % 2:
        res.extend([x])
    else:
        res.extend([x, x])

或者,您可以创建一个列表列表并使用itertools.chain.from_iterable将其展平:

In [54]: import itertools

In [55]: list(itertools.chain.from_iterable([x] * (2 - x % 2) for x in range(10)))
Out[55]: [0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9]

感谢评论中的人对重构这些解决方案的帮助。你知道吗

相关问题 更多 >