How to determine if a value is ONE-BUT-LAST in a Python generator?

Since the generator returns values ​​lazily, how can I determine if the value returned by the generator is one-but-last? I spent an hour on this and cannot understand.

Any help appreciated. Is it possible?

Thank Boda Sido!

+2
source share
3 answers

You can wrap the generator in a generator that generates a sequence of pairs, the first element of which is logical, indicating whether the element is the last but one:

def ending(generator): z2 = generator.next() z1 = generator.next() for x in generator: yield (False, z2) z2, z1 = z1, x yield (True, z2) yield (False, z1) 

Let me test it on a simple iterator:

 >>> g = iter('abcd') >>> g <iterator object at 0x9925b0> 

You should receive:

 >>> for is_last_but_one, char in ending(g): ... if is_last_but_one: ... print "The last but one is", char ... The last but one is c 

Too what happens under the hood:

 >>> g = iter('abcd') >>> for x in ending(g): ... print x ... (False, 'a') (False, 'b') (True, 'c') (False, 'd') 
+10
source

If you want to see arbitrary future values ​​of the iterator without using them, you can wrap the iterator in a peekable iterator, which can buffer future values.

 import collections class PeekIter(object): def __init__(self, iterable): self._iter = iter(iterable) self._peekbuf = collections.deque() def next(self): if self._peekbuf: return self._peekbuf.popleft() else: return self._iter.next() def peek(self, future=0, default=None): try: while len(self._peekbuf) <= future: self._peekbuf.append(self._iter.next()) return self._peekbuf[future] except StopIteration: return default 

You can then look into future meanings without consuming them.

 >>> p = PeekIter(range(3)) >>> p.peek() 0 >>> p.next() 0 >>> p.peek(0) 1 >>> p.peek(0) 1 >>> p.peek(1) 2 >>> p.peek(2) >>> sentinel = object() >>> sentinel <object object at 0x28470> >>> p.peek(1, sentinel) 2 >>> p.peek(2, sentinel) <object object at 0x28470> 
+2
source

Itertools based solution

 from itertools import tee, islice, repeat, chain, izip def gen_with_offset(gen, offset): gen1, gen2 = tee(gen) gen2 = (False for x in gen2) gen2 = chain(islice(gen2, offset, None), [True], repeat(False)) for g, sentinel in izip(gen1, gen2): yield g, sentinel Usage: >>> gex = iter('abcedefg') >>> for p in gen_with_offset(gex, 4): ... print p ... ('a', False) ('b', False) ('c', False) ('e', False) ('d', True) ('e', False) ('f', False) ('g', False) >>> 
+1
source

Source: https://habr.com/ru/post/1303795/


All Articles