用于确定空列表条目是否为"连续"的Pythonic方式

Cla*_*ell 45 python null list slice

我正在寻找一种方法来轻松确定列表中的所有非None项是否出现在单个连续切片中. 我将使用整数作为非None项的示例.

例如,该列表[None, None, 1, 2, 3, None, None]符合我对连续整数条目的要求.与此相反,[1, 2, None, None, 3, 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,以及我们是否遇到了一个问题int- 这最终导致了一个高度嵌套且非常难以遵循的if/else系列嵌入在for循环中的语句.(除了丑陋之外,我承认在每种情况下都没有让它工作).

任何人都知道一种更简单的方法来确定列表中的非None项是否出现在单个连续切片中?

Joh*_*ooy 44

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

这里有几个例子.您可以使用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
Run Code Online (Sandbox Code Playgroud)

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

  • +1,因为这非常聪明,我从来没有想过它.对于推进迭代器的副作用目的,我并没有因为使用"any"和"all"而完全卖掉,但我没有一个好的论据反对它. (12认同)
  • 在试着查看它是如何工作之后,我有点难过 - 根据文档,all()和any()根据输入的内容返回True或False ......然而它们似乎被使用了在这里修改他们的输入.你知道我在哪里可以找到一些文档来解释它是如何工作的吗? (5认同)
  • 如此简单......然而如此正确.我希望我能想到它. (4认同)
  • @Simon,它使用all和any的_defined_行为.为什么你认为这是隐含的?你是什​​么意思? (4认同)
  • @Simon It**明确说明.文档*声明*对于那些代码,"any"和"all"等同于**.等效意味着使用内置函数或示例代码应始终产生相同的结果.由于示例代码是惰性的,因此内置的"any"和"all"必须是懒惰的.doc的编写者简单地决定,不是一个接一个地指出关于这些简单函数的所有角落案例和事物,而是用一小段可读代码更好地解释它们的属性. (4认同)
  • -1因为乍一看是不可读的,恕我直言不符合"pythonic方式" (2认同)

DSM*_*DSM 25

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

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

[编辑]

由于评论中似乎有一些讨论,我将解释为什么我比其他人更喜欢这种方法.

我们试图找出是否存在一组连续的非None对象,以及

sum(1 for k,g in groupby(seq, lambda x: x is not None) if k)
Run Code Online (Sandbox Code Playgroud)

使用stdlib中的函数计算连续的非None对象的数量,该函数用于收集连续的组.我们一看到groupby,就会认为"连续的群体",反之亦然.从这个意义上讲,它是自我记录的.这基本上是我的目标的定义.

恕我直言唯一的弱点是它不会短路,而且可以修复,但在考虑之后我仍然更喜欢这个,因为它使用了我喜欢的原语 - "计算连续非非组的数量" - 我更喜欢"只要你能告诉我是否有一个以上的非连续非群组".

实现最后一个方法的许多方法依赖于对该问题的巧妙观察,例如"如果只有一个连续的非-Onob对象组,那么如果我们扫描直到找到第一个not-None对象,然后扫描对象直到我们找到第一个非None组(如果存在),那么剩下的是否为None给我们答案." (或类似的东西,这是我的问题的一部分:我必须考虑它.)对我来说,感觉就像使用"实现细节"解决问题,并关注我们可以用来解决的问题的属性它,而不是简单地向Python指定问题并让Python完成工作.

我是一个脑力很小的熊,正如俗话所说,我喜欢避免变得聪明,因为根据我的经验,这是一条充满失败的路线.

一如既往,每个人的里程可能会有所不同,当然,也可能与他们的聪明程度成比例.


Ble*_*der 12

你可以使用类似的东西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
Run Code Online (Sandbox Code Playgroud)

这将仅迭代直到它看到一个组两次.我不确定你是否考虑[None, None],所以根据你的需要进行调整.

  • 我喜欢这个,因为它比@ DSM和"短路"更清晰. (2认同)

mgi*_*son 7

这可能不是最好的方法,但您可以查找第一个非None条目和最后一个non-None条目,然后检查切片None.例如:

def is_continuous(seq):
    try:
        first_none_pos = next(i for i,x in enumerate(seq) if x is not None)
        #need the or None on the next line to handle the case where the last index is `None`.
        last_none_pos = -next(i for i,x in enumerate(reversed(seq)) if x is not None) or None
    except StopIteration: #list entirely of `Nones`
        return False
    return None not in seq[first_none_pos:last_none_pos]

assert is_continuous([1,2,3,None,None]) == True
assert is_continuous([None, 1,2,3,None]) == True
assert is_continuous([None, None, 1,2,3]) == True
assert is_continuous([None, 1, None, 2,3]) == False
assert is_continuous([None, None, 1, None, 2,3]) == False
assert is_continuous([None, 1, None, 2, None, 3]) == False
assert is_continuous([1, 2, None, 3, None, None]) == False
Run Code Online (Sandbox Code Playgroud)

这适用于任何序列类型.


eca*_*mur 7

使用序列元素的自然方法是使用dropwhile:

from itertools import dropwhile
def continuous(seq):
    return all(x is None for x in dropwhile(lambda x: x is not None,
                                            dropwhile(lambda x: x is None, seq)))
Run Code Online (Sandbox Code Playgroud)

我们可以在没有嵌套函数调用的情况下表达:

from itertools import dropwhile
def continuous(seq):
    core = dropwhile(lambda x: x is None, seq)
    remainder = dropwhile(lambda x: x is not None, core)
    return all(x is None for x in remainder)
Run Code Online (Sandbox Code Playgroud)


ugo*_*ren 5

一个班轮:

contiguous = lambda l: ' ' not in ''.join('x '[x is None] for x in l).strip()
Run Code Online (Sandbox Code Playgroud)

真正的工作是由strip功能完成的.如果剥离的字符串中有空格,则它们不是前导/尾随.函数的其余部分将列表转换为字符串,每个字符串都有一个空格None.