Pythonic方法判断非空列表条目是否“连续”
我想找个简单的方法来判断一个列表中所有非空(不是None)的项目是否在一个连续的片段中。 我会用整数来举例说明这些非空的项目。
比如,列表 [None, None, 1, 2, 3, None, None]
符合我对连续整数的要求。相对而言,[1, 2, None, None, 3, None]
是不连续的,因为在整数之间有None的项目。
再举几个例子,让这个概念更清晰。
连续的:
[1, 2, 3, None, None]
[None, None, 1, 2, 3]
[None, 1, 2, 3, None]
不连续的:
[None, 1, None, 2, None, 3]
[None, None, 1, None, 2, 3]
[1, 2, None, 3, None, None]
我最开始的方法是用变量来跟踪我们是否遇到过None,以及是否遇到过整数——结果是嵌套了很多层的if/else语句,这让人很难理解,尤其是在for循环里。(而且我承认,这种方法在某些情况下并没有成功)。
有没有人知道更简单的方法来判断列表中的非None项目是否在一个连续的片段中?
11 个回答
你可以使用类似于 itertools.groupby
的东西:
from itertools import groupby
def are_continuous(items):
saw_group = False
for group, values in groupby(items, lambda i: i is not None):
if group:
if saw_group:
return False
else:
saw_group = True
return True
这个方法会一直循环,直到它看到同样的组出现两次为止。我不确定你是否会考虑 [None, None]
,所以你可以根据自己的需要进行调整。
老朋友 itertools.groupby
来帮忙了:
from itertools import groupby
def contiguous(seq):
return sum(1 for k,g in groupby(seq, lambda x: x is not None) if k) == 1
结果是
>>> contiguous([1,2,3,None,None])
True
>>> contiguous([None, 1,2,3,None])
True
>>> contiguous([None, None, 1,2,3])
True
>>> contiguous([None, 1, None, 2,3])
False
>>> contiguous([None, None, 1, None, 2,3])
False
>>> contiguous([None, 1, None, 2, None, 3])
False
>>> contiguous([1, 2, None, 3, None, None])
False
[编辑]
因为评论里似乎有一些讨论,我来解释一下为什么我更喜欢这种方法,而不是其他一些方法。
我们想知道是否有一组连续的非空对象,
sum(1 for k,g in groupby(seq, lambda x: x is not None) if k)
这个方法会计算连续的非空对象的数量,使用的是标准库中专门用来收集连续组的函数。当我们看到 groupby
时,就会想到“连续组”,反之亦然。从这个角度来看,它的意思很清楚。这基本上就是我目标的 定义。
在我看来,唯一的缺点是它没有短路功能,这个问题可以解决,但经过一番思考,我还是更喜欢这个方法,因为它使用了我喜欢的一个基本概念——“计算连续的非空组的数量”,而不是简单地“告诉我是否有超过一个连续的非空组,尽快给我答案”。
很多实现最后一种方法的思路依赖于对问题的巧妙观察,比如“如果只有一个连续的非空对象组,那么我们可以先扫描找到第一个非空对象,然后继续扫描直到找到第一个非空组,如果存在的话,最后看看剩下的是否是空的,这样就能得到答案。”(或者类似的说法,这也是我遇到的问题之一:我得思考一下。)对我来说,这感觉像是用“实现细节”来解决问题,关注的是我们可以用来解决问题的特性,而不是简单地把问题交给Python,让它来处理。
正如那句老话说的,我的脑袋不太灵光,我喜欢避免聪明的解决方案,因为根据我的经验,这条路上布满了失败。
当然,每个人的理解和经验可能不同,可能和他们的聪明才智成正比。
def contiguous(seq):
seq = iter(seq)
all(x is None for x in seq) # Burn through any Nones at the beginning
any(x is None for x in seq) # and the first group
return all(x is None for x in seq) # everthing else (if any) should be None.
这里有几个例子。你可以用 next(seq)
来获取迭代器中的下一个项目。我会在每个例子后面标记出下一个项目。
例子1:
seq = iter([None, 1, 2, 3, None]) # [None, 1, 2, 3, None]
# next^
all(x is None for x in seq)
# next^
any(x is None for x in seq)
# next^ (off the end)
return all(x is None for x in seq) # all returns True for the empty sequence
例子2:
seq = iter([1, 2, None, 3, None, None]) # [1, 2, None, 3, None, None]
# next^
all(x is None for x in seq)
# next^
any(x is None for x in seq)
# next^
return all(x is None for x in seq) # all returns False when 3 is encountered