Python:列表理解以生成偶数的连续子列表

Python:列表理解以生成偶数的连续子列表,python,python-3.x,list,list-comprehension,Python,Python 3.x,List,List Comprehension,我试图练习Python练习,但使用列表理解来解决问题,而不是书中所示的初学者风格的循环。有一个例子,它要求将数字列表仅放入偶数列表中,但它们必须位于子列表中,这样,如果数字紧随其后而没有被奇数打断,则它们应一起放入子列表中: my_list = [2,3,5,7,8,9,10,12,14,15,17,25,31,32] desired_output = [[2],[8],[10,12,14],[32]] 所以你可以在上面的期望输出中看到,10,12,14都是偶数,它们彼此相连,没有被奇数打断,

我试图练习Python练习,但使用列表理解来解决问题,而不是书中所示的初学者风格的循环。有一个例子,它要求将数字列表仅放入偶数列表中,但它们必须位于子列表中,这样,如果数字紧随其后而没有被奇数打断,则它们应一起放入子列表中:

my_list = [2,3,5,7,8,9,10,12,14,15,17,25,31,32]
desired_output = [[2],[8],[10,12,14],[32]]
所以你可以在上面的期望输出中看到,10,12,14都是偶数,它们彼此相连,没有被奇数打断,所以它们一起被放入一个子列表中。8的两边各有一个奇数,所以在排除赔率后,它将单独放入子列表中

我可以使用下面这样的列表理解轻松地组合一个evens列表,但我不知道如何将其放入子列表中,如所需的输出所示。有人能用列表理解(或者生成器,我不介意,因为我现在正在努力学习这两种方法)对此提出一个建议吗。谢谢


正如在评论中所解释的,列表理解不应该被视为“初学者”——首先关注使用简单的
for
循环编写逻辑

当你准备好了,你可以看看基于理解的方法。这里有一个:

from itertools import groupby

my_list = [2,3,5,7,8,9,10,12,14,15,17,25,31,32]

condition = lambda x: all(i%2==0 for i in x)
grouper = (list(j) for _, j in groupby(my_list, key=lambda x: x%2))

res = filter(condition, grouper)

print(list(res))

# [[2], [8], [10, 12, 14], [32]]

此解决方案中需要注意的要点是,在调用
list(res)
之前,不会计算任何内容。这是因为过滤器和生成器理解是懒惰的。

你提到过你也想学习生成器,所以这里有一个版本也更具可读性,imho

from itertools import groupby


def is_even(n):
    return n%2 == 0


def runs(lst):
    for even, run in groupby(lst, key=is_even):
        if even:
            yield list(run)


if __name__ == '__main__':
    lst = [2, 3, 5, 7, 8, 9, 10, 12, 14, 15, 17, 25, 31, 32]
    res = list(runs(lst))
    print(res)
顺便说一句,如果您绝对、积极地希望将其作为列表理解来实施,那么这种解决方案很自然地就不符合上述要求:

[list(run) for even, run in groupby(lst, key=is_even) if even]

如果您不想使用itertools,还有另一种方法可以使用列表理解

首先,取奇数元素的索引:

[i for i,x in enumerate(my_list) if x%2==1]
并添加两个哨兵:
[-1]
前面和
[len(我的列表)]
后面:

odd_indices = [-1]+[i for i,x in enumerate(my_list) if x%2==1]+[len(my_list)]
# [-1, 1, 2, 3, 5, 9, 10, 11, 12, 14]
你现在有了这样的东西:

 [2,3,5,7,8,9,10,12,14,15,17,25,31,32]
^---^-^-^---^-----------^--^--^--^----^
你可以看到你的序列。现在,取这些索引之间的元素。为此,zip
odd_index
自身将间隔作为元组:

zip(odd_indices, odd_indices[1:])
# [(-1, 1), (1, 2), (2, 3), (3, 5), (5, 9), (9, 10), (10, 11), (11, 12), (12, 14)]    

even_groups = [my_list[a+1:b] for a,b in zip(odd_indices, odd_indices[1:])]
# [[2], [], [], [8], [10, 12, 14], [], [], [], [32]]
您只需过滤非空列表:

even_groups = [my_list[a+1:b] for a,b in zip(odd_indices, odd_indices[1:]) if a+1<b]
# [[2], [8], [10, 12, 14], [32]]

正如@jpp所指出的,在您感到舒适之前,请选择基本循环。也许永远避免那些嵌套的列表理解

对于循环来说,没有什么“初学者风格”。对于这个特定的问题,请使用
itertools.groupby()
我建议首先学习旧循环,它是基础。(假设您从正确的理解开始)非常感谢添加此解决方案。我的书在第4章中介绍了列表理解,因此与书后面更可怕的主题相比,我认为它仍然是一个初学者(我也有点初学者不耐烦)。这本书已经介绍了lambda和keys,但我还没有见过groupby(尽管我习惯于在SQL中使用groupby)。@Shaken\u not\u stived,你可能会发现在
itertools.groupby
上的应用程序很有用。这也是一个很好的选择。我理解定义函数和产量的概念。我只需要重新阅读一下name和main(我见过它们,只是还没用过)。谢谢你把它们分解成容易消化的部分。这使它更容易理解。
even_groups = [my_list[a+1:b] for a,b in zip(odd_indices, odd_indices[1:]) if a+1<b]
# [[2], [8], [10, 12, 14], [32]]
>>> my_list = [2,3,5,7,8,9,10,12,14,15,17,25,31,32]
>>> [my_list[a+1:b] for l1 in [[-1]+[i for i,x in enumerate(my_list) if x%2==1]+[len(my_list)]] for a,b in zip(l1, l1[1:]) if b>a+1]
[[2], [8], [10, 12, 14], [32]]