Как определить, является ли значение ONE-BUT-LAST в генераторе Python? - PullRequest
2 голосов
/ 11 марта 2010

Поскольку генератор возвращает значения лениво, как мне определить, является ли значение, возвращаемое из генератора, одним, но последним? Я потратил на это как час и не могу понять.

Любая помощь приветствуется. Это вообще возможно?

Спасибо, Бода Цидо!

Ответы [ 3 ]

10 голосов
/ 11 марта 2010

Вы можете заключить генератор в генератор, который генерирует последовательность пар, первый элемент которых является логическим значением, сообщающим вам, является ли элемент последним, но одним:

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)

Давайте проверим это на простом итераторе:

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

Вы должны получить:

>>> 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

Тоже посмотрим, что происходит под капотом:

>>> g = iter('abcd')
>>> for x in ending(g):
...     print x
... 
(False, 'a')
(False, 'b')
(True, 'c')
(False, 'd')
2 голосов
/ 11 марта 2010

Если вы хотите увидеть произвольные будущие значения итератора без их использования, вы можете заключить итератор в «идиотный» итератор, который может буферизовать будущие значения.

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

Затем вы можете посмотреть на будущие значения, не потребляя их.

>>> 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>
1 голос
/ 15 июня 2012

Решение на основе itertools

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)
>>>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...