Python sentinel连续出现两次时停止迭代的简洁方法

Python sentinel连续出现两次时停止迭代的简洁方法,python,iterator,generator,itertools,generator-expression,Python,Iterator,Generator,Itertools,Generator Expression,我正在寻找一种方法来生成一个迭代器,该迭代器接受一个iterable,并只传递这些值,直到一个sentinel值直接连续出现两次。类似于国际热核实验堆(iter)(下一个哨兵)只有哨兵必须出现两次 下面这段相当没有创意的代码确实起到了作用,但肯定有一个不那么冗长的解决方案 因此,把它放在一个具体的问题上: 有没有一种方法可以避免使用完全成熟的生成器,并使用itertools或生成器表达式实现同样的效果 >>> def repeat_offenders(a, sentinel):

我正在寻找一种方法来生成一个迭代器,该迭代器接受一个iterable,并只传递这些值,直到一个sentinel值直接连续出现两次。类似于国际热核实验堆(iter)(下一个哨兵)只有哨兵必须出现两次

下面这段相当没有创意的代码确实起到了作用,但肯定有一个不那么冗长的解决方案

因此,把它放在一个具体的问题上:

有没有一种方法可以避免使用完全成熟的生成器,并使用
itertools
或生成器表达式实现同样的效果

>>> def repeat_offenders(a, sentinel):
...    ia = iter(a)
...    for x in ia:
...       if x==sentinel:
...          try:
...             y = next(ia)
...          except StopIteration:
...             yield x
...             raise
...          if y==sentinel:
...             raise StopIteration
...          yield x
...          yield y
...       else:
...          yield x
以下是两个例子:

>>> ''.join(repeat_offenders('ABCABCAABBCC', 'B'))
'ABCABCAA'
>>> ''.join(repeat_offenders('ABABAB', 'B'))
'ABABAB'

注释类似,但缺少生成器角度。

您可以根据
iwindow
定义
repeat\u违犯者,滑动窗口配方(可用于任何iterable,而不仅仅是序列)和通常的
iter(可调用,哨兵)
习惯用法:

import itertools as IT

def iwindow(iterable, n=2):
    """
    Returns a sliding window (of width n) over data from the iterable.
    s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ..., (sk, None, ..., None)
    """
    iterables = IT.tee(iterable, n)
    iterables = (IT.islice(it, pos, None) for pos, it in enumerate(iterables))
    yield from IT.zip_longest(*iterables)

def repeat_offenders(iterable, sentinel, repeat=2):
    return (item[0] for item in iter(iwindow(iterable, repeat).__next__, 
                                     (sentinel,)*repeat))

print(''.join(repeat_offenders('ABCABCAABBCC', 'B', 2)))
# ABCABCAA

print(''.join(repeat_offenders('ABABAB', 'B', 2)))
# ABABAB


iwindow
是itertools文档中所示内容的概括。通过使用
iwindow
编写
repeat\u
,我们可以将这个概念概括为在
n
几乎免费重复之后停止。

不确定是否有帮助,但我相信您的代码可以写得更简洁,如下所示:

from itertools import zip_longest

def repeat_offenders_jp(a, s):
    for i, j in zip_longest(a, a[1:]):
        if i == j and i == s:
            break
        else:
            yield i

''.join(repeat_offenders_jp('ABCABCAABBCC', 'B'))  # 'ABCABCAA'
''.join(repeat_offenders_jp('ABABAB', 'B'))        # 'ABABAB'

此处使用
zip\u longest
作为@jp\u data\u分析建议,但作为“一行”和
takewhile

from itertools import zip_longest, takewhile

sentinel = 'B'
string = 'ABCABCAABBCC'
"".join(t[0] for t
        in takewhile(lambda t: t[0] != sentinel or t[0] != t[1], 
        zip_longest(string, string[1:])))
# 'ABCABCAA'

string = 'ABABAB'
"".join(t[0] for t
        in takewhile(lambda t: t[0] != sentinel or t[0] != t[1], 
        zip_longest(string, string[1:])))
# 'ABABAB'

Darkonaut,@jp_data_analysis,也许我没有说得足够清楚(好吧,我在问题中说iterable),但我正在寻找一些迭代证明的东西,这意味着直接索引、压缩自己等都不可用<代码>字符串
仅用于说明。@Paul Panzer字符串是可编辑的。否则你就不能在它们上面迭代。我想你想问一个迭代器,它接受一个迭代器?是的,字符串是iterables,但iterables不一定是字符串。如果某个东西对字符串有效,这并不意味着它对iterables一般有效。例如,有一些类似于文件对象的iterables没有
\uuu getitem\uuuu
方法。提供一个我们无法使用的示例有什么意义?我们必须假设这就是你正在使用的iterable。我建议你用一个合适的例子提出一个新问题。每个例子都会有一些不能代表例子所代表的概念的特点。不依赖这些是常识问题。谢谢你的解决方案。它实际上比我简陋的生成器慢了一点(我想这应该归咎于
tee
),但简单地推广到两次以上的重复是一个不错的奖励。正如你所说的,
iwindow
是一件有用的事情。