可以产生产生多个连续的发电机?(Can yield produce multiple consecu

2019-07-29 12:57发布

下面是分裂迭代项目子列表两种功能。 我认为,这种类型的任务多次编程。 我用它来分析日志包括文件repr线,如(“结果”,“案例”,123,4.56)和(“垃圾”,..)等等。

我想改变这些,让他们将产生的迭代器,而非列表。 由于列表可能增长相当大的,但我或许可以决定接受或者跳过它基于前几个项目。 此外,如果ITER版本可我想窝他们,但是会通过复制部分会浪费一些内存这些列表的版本。

但是,从一个迭代源获得多台发电机wan't容易的,我的,所以我寻求帮助。 如果可能的话,我想避免引入新的类。

另外,如果你知道这个问题的一个更好的标题,请告诉我。

谢谢!

def cleave_by_mark (stream, key_fn, end_with_mark=False):
    '''[f f t][t][f f] (true) [f f][t][t f f](false)'''
    buf = []
    for item in stream:
        if key_fn(item):
            if end_with_mark: buf.append(item)
            if buf: yield buf
            buf = []
            if end_with_mark: continue
        buf.append(item)
    if buf: yield buf

def cleave_by_change (stream, key_fn):
    '''[1 1 1][2 2][3][2 2 2 2]'''
    prev = None
    buf = []
    for item in stream:
        iden = key_fn(item)
        if prev is None: prev = iden
        if prev != iden:
            yield buf
            buf = []
            prev = iden
        buf.append(item)
    if buf: yield buf

编辑:我自己的答案

感谢大家的回答,我可以写我问了! 当然,作为“cleave_for_change”功能我还可以使用itertools.groupby

def cleave_by_mark (stream, key_fn, end_with_mark=False):
    hand = []
    def gen ():
        key = key_fn(hand[0])
        yield hand.pop(0)
        while 1:
            if end_with_mark and key: break
            hand.append(stream.next())
            key = key_fn(hand[0])
            if (not end_with_mark) and key: break
            yield hand.pop(0)
    while 1:
        # allow StopIteration in the main loop
        if not hand: hand.append(stream.next())
        yield gen()

for cl in cleave_by_mark (iter((1,0,0,1,1,0)), lambda x:x):
    print list(cl),  # start with 1
# -> [1, 0, 0] [1] [1, 0]
for cl in cleave_by_mark (iter((0,1,0,0,1,1,0)), lambda x:x):
    print list(cl),
# -> [0] [1, 0, 0] [1] [1, 0]
for cl in cleave_by_mark (iter((1,0,0,1,1,0)), lambda x:x, True):
    print list(cl),  # end with 1
# -> [1] [0, 0, 1] [1] [0]
for cl in cleave_by_mark (iter((0,1,0,0,1,1,0)), lambda x:x, True):
    print list(cl),
# -> [0, 1] [0, 0, 1] [1] [0]

/

def cleave_by_change (stream, key_fn):
    '''[1 1 1][2 2][3][2 2 2 2]'''
    hand = []
    def gen ():
        headkey = key_fn(hand[0])
        yield hand.pop(0)
        while 1:
            hand.append(stream.next())
            key = key_fn(hand[0])
            if key != headkey: break
            yield hand.pop(0)
    while 1:
        # allow StopIteration in the main loop
        if not hand: hand.append(stream.next())
        yield gen()

for cl in cleave_by_change (iter((1,1,1,2,2,2,3,2)), lambda x:x):
    print list(cl),
# -> [1, 1, 1] [2, 2, 2] [3] [2]

注意:如果任何人要使用这些,一定要在各个层面用尽发电机 ,安德鲁指出。 因为否则内发电机左代替下一个“块”开始,其中外发生器高产回路将重新启动右。

stream = itertools.product('abc','1234', 'ABCD')
for a in iters.cleave_by_change(stream, lambda x:x[0]):
    for b in iters.cleave_by_change(a, lambda x:x[1]):
        print b.next()
        for sink in b: pass
    for sink in a: pass

('a', '1', 'A')
('b', '1', 'A')
('c', '1', 'A')

Answer 1:

亚当的回答是好。 这是以防万一你很好奇如何通过做手工:

def cleave_by_change(stream):
    def generator():
        head = stream[0]
        while stream and stream[0] == head:
            yield stream.pop(0)
    while stream:
        yield generator()

for g in cleave_by_change([1,1,1,2,2,3,2,2,2,2]):
    print list(g)

这使:

[1, 1, 1]
[2, 2]
[3]
[2, 2, 2, 2]

(以前的版本需要一个黑客,或者在Python 3, nonlocal因为我分配给stream里面generator()这使得(第二变量也称为) stream本地generator()默认-信贷在评论gnibbler)。

注意,这种做法是危险的 - 如果你不“消费”被退回,那么你将得到越来越多的发电机,因为流没有得到任何较小。



Answer 2:

关于你的第二功能,您可以使用itertools.groupby非常轻松地做到这一点。

这里有一个替代实现,现在产生的发电机,而不是列表:

from itertools import groupby

def cleave_by_change2(stream, key_fn):
    return (group for key, group in groupby(stream, key_fn))

下面是它在行动(一路上自由的打印,所以你可以看到这是怎么回事):

main_gen = cleave_by_change2([1,1,1,2,2,3,2,2,2,2], lambda x: x)

print main_gen

for sub_gen in main_gen:
    print sub_gen
    print list(sub_gen)

这将产生:

<generator object <genexpr> at 0x7f17c7727e60>
<itertools._grouper object at 0x7f17c77247d0>
[1, 1, 1]
<itertools._grouper object at 0x7f17c7724850>
[2, 2]
<itertools._grouper object at 0x7f17c77247d0>
[3]
<itertools._grouper object at 0x7f17c7724850>
[2, 2, 2, 2]


Answer 3:

我实现了我的描述:

如果你想要的是返回之前,甚至建立,通过提供过滤参数,这将是可能的功能来拒绝列表。 当这个过滤器将拒绝列表前缀功能会折腾出的电流输出列表,直到下一组启动跳过追加到输出列表。

def cleave_by_change (stream, key_fn, filter=None):
    '''[1 1 1][2 2][3][2 2 2 2]'''
    S = object()
    skip = False
    prev = S
    buf = []
    for item in stream:
        iden = key_fn(item)
        if prev is S:
           prev = iden
        if prev != iden:
            if not skip:
                yield buf
            buf = []
            prev = iden
            skip = False
        if not skip and filter is not None:
           skip = not filter(item)
        if not skip:
           buf.append(item)
    if buf: yield buf

print list(cleave_by_change([1, 1, 1, 2, 2, 3, 2, 2, 2, 2], lambda a: a, lambda i: i != 2))
# => [[1, 1, 1], [3]]
print list(cleave_by_change([1, 1, 1, 2, 2, 3, 2, 2, 2, 2], lambda a: a, lambda i: i == 2))
# => [[2, 2], [2, 2, 2, 2]]


文章来源: Can yield produce multiple consecutive generators?