Can yield produce multiple consecutive generators?
下面是两个函数,它们将可ITable项拆分为子列表。我相信这种类型的任务被编程了很多次。我使用它们来解析由
我想更改这些,这样它们将产生迭代器而不是列表。因为这个列表可能会变大,但我可以根据前几个项目决定接受它或跳过它。另外,如果ITER版本可用,我想嵌套它们,但是使用这些列表版本,复制部分会浪费一些内存。
但是从一个无法识别的源广域网派生多个生成器对我来说并不容易,所以我请求帮助。如果可能的话,我希望避免引入新的类。
另外,如果你知道这个问题更好的标题,请告诉我。
谢谢您!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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 |
编辑:我自己的答案
感谢大家的回答,我可以写下我想要的!当然,对于"切变"功能,我也可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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] |
/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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] |
小心:如安德鲁指出的,如果有人要使用这些设备,一定要在每一级对发电机进行排气。否则,外部发电机产生回路将从内部发电机左侧的右侧重新启动,而不是从下一个"块"开始的位置重新启动。
1 2 3 4 5 6 7 8 9 10 | 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') |
亚当的回答很好。这只是为了以防万一你想知道怎么用手来做:
1 2 3 4 5 6 7 8 9 10 | 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 2 3 4 | [1, 1, 1] [2, 2] [3] [2, 2, 2, 2] |
(以前的版本需要一个hack,或者,在python 3中,
请注意,这种方法是危险的——如果您不"消耗"返回的生成器,那么您将得到越来越多,因为流不会变得越来越小。
对于第二个功能,您可以使用
下面是一个替代实现,它现在生成生成器而不是列表:
1 2 3 4 | from itertools import groupby def cleave_by_change2(stream, key_fn): return (group for key, group in groupby(stream, key_fn)) |
这是它的实际应用(一路自由印刷,所以你可以看到发生了什么):
1 2 3 4 5 6 7 | 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) |
收益率:
1 2 3 4 5 6 7 8 9 | <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] |
我实现了我所描述的:
If what you want is to reject a list before it is returned or even
build, by providing a filter argument to the functions that would be
possible. When this filter rejects a list prefix the function would
toss out the current output list and skip appending to the output list
until the next group is started.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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]] |