태그 보관물: generator

generator

Python 생성기에서 한 요소 (peek)를 미리 보는 방법은 무엇입니까? = iter([1,2,3]) next_value

파이썬 생성기에서 한 요소를 미리 보는 방법을 알 수 없습니다. 내가 보자 마자 사라졌습니다.

내가 의미하는 바는 다음과 같습니다.

gen = iter([1,2,3])
next_value = gen.next()  # okay, I looked forward and see that next_value = 1
# but now:
list(gen)  # is [2, 3]  -- the first value is gone!

다음은 더 실제적인 예입니다.

gen = element_generator()
if gen.next_value() == 'STOP':
  quit_application()
else:
  process(gen.next())

하나의 요소를 앞으로 볼 수있는 생성기를 작성하도록 도와 줄 사람이 있습니까?



답변

Python 생성기 API는 한 가지 방법입니다. 읽은 요소를 푸시 백 할 수 없습니다. 그러나 itertools 모듈을 사용하여 새 반복자를 만들고 요소 앞에 추가 할 수 있습니다 .

import itertools

gen = iter([1,2,3])
peek = gen.next()
print list(itertools.chain([peek], gen))

답변

완전성을 위해 more-itertools패키지 (아마도 Python 프로그래머 도구 상자의 일부 여야 함)에는 peekable이 동작을 구현 하는 래퍼가 포함되어 있습니다. 문서 의 코드 예제는 다음을 보여줍니다.

>>> p = peekable(['a', 'b'])
>>> p.peek()
'a'
>>> next(p)
'a'

그러나 실제로 필요하지 않도록이 기능을 사용하는 코드를 다시 작성할 수 있습니다. 예를 들어, 질문의 실제 코드 샘플은 다음과 같이 작성할 수 있습니다.

gen = element_generator()
command = gen.next_value()
if command == 'STOP':
  quit_application()
else:
  process(command)

(독자 주 : 구버전의 Python을 참조하더라도이 글을 작성할 때의 질문에서 예제의 구문을 보존했습니다.)


답변

좋아-2 년이 늦었지만이 질문을 보았지만 만족스러운 답을 찾지 못했습니다. 이 메타 생성기로 나타났습니다.

class Peekorator(object):

    def __init__(self, generator):
        self.empty = False
        self.peek = None
        self.generator = generator
        try:
            self.peek = self.generator.next()
        except StopIteration:
            self.empty = True

    def __iter__(self):
        return self

    def next(self):
        """
        Return the self.peek element, or raise StopIteration
        if empty
        """
        if self.empty:
            raise StopIteration()
        to_return = self.peek
        try:
            self.peek = self.generator.next()
        except StopIteration:
            self.peek = None
            self.empty = True
        return to_return

def simple_iterator():
    for x in range(10):
        yield x*3

pkr = Peekorator(simple_iterator())
for i in pkr:
    print i, pkr.peek, pkr.empty

결과 :

0 3 False
3 6 False
6 9 False
9 12 False
...
24 27 False
27 None False

즉, 반복하는 동안 언제든지 목록의 다음 항목에 액세스 할 수 있습니다.


답변

itertools.tee를 사용하여 생성기의 경량 사본을 생성 할 수 있습니다. 그런 다음 한 사본을 미리 들여다 보면 두 번째 사본에는 영향을 미치지 않습니다.

import itertools

def process(seq):
    peeker, items = itertools.tee(seq)

    # initial peek ahead
    # so that peeker is one ahead of items
    if next(peeker) == 'STOP':
        return

    for item in items:

        # peek ahead
        if next(peeker) == "STOP":
            return

        # process items
        print(item)

‘항목’생성기는 ‘피커’를 성추행해도 영향을받지 않습니다. ‘티’를 호출 한 후 원래 ‘seq’를 사용하면 안됩니다.

FWIW, 이것은 이 문제를 해결 하는 잘못된 방법입니다. 생성기에서 1 개 항목을 미리 봐야하는 알고리즘은 현재 생성기 항목과 이전 항목을 사용하도록 작성 될 수 있습니다. 그러면 생성기 사용을 망칠 필요가 없으며 코드가 훨씬 간단 해집니다. 이 질문에 대한 다른 답변을 참조하십시오.


답변

>>> gen = iter(range(10))
>>> peek = next(gen)
>>> peek
0
>>> gen = (value for g in ([peek], gen) for value in g)
>>> list(gen)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

답변

재미를 위해 Aaron의 제안에 따라 lookahead 클래스 구현을 만들었습니다.

import itertools

class lookahead_chain(object):
    def __init__(self, it):
        self._it = iter(it)

    def __iter__(self):
        return self

    def next(self):
        return next(self._it)

    def peek(self, default=None, _chain=itertools.chain):
        it = self._it
        try:
            v = self._it.next()
            self._it = _chain((v,), it)
            return v
        except StopIteration:
            return default

lookahead = lookahead_chain

이를 통해 다음이 작동합니다.

>>> t = lookahead(xrange(8))
>>> list(itertools.islice(t, 3))
[0, 1, 2]
>>> t.peek()
3
>>> list(itertools.islice(t, 3))
[3, 4, 5]

이 구현에서는 peek를 연속으로 여러 번 호출하는 것이 좋지 않습니다.

CPython 소스 코드를 살펴보면서 더 짧고 효율적인 방법을 찾았습니다.

class lookahead_tee(object):
    def __init__(self, it):
        self._it, = itertools.tee(it, 1)

    def __iter__(self):
        return self._it

    def peek(self, default=None):
        try:
            return self._it.__copy__().next()
        except StopIteration:
            return default

lookahead = lookahead_tee

사용법은 위와 동일하지만 연속해서 여러 번 peek를 사용하기 위해 여기에서 가격을 지불하지 않습니다. 몇 줄을 더 사용하면 반복기에서 둘 이상의 항목을 미리 볼 수도 있습니다 (사용 가능한 RAM까지).


답변

항목 (i, i + 1)을 사용하는 대신, 여기서 ‘i’는 현재 항목이고 i + 1은 ‘앞서보기’버전 인 경우 (i-1, i)를 사용해야합니다. 여기서 ‘i-1’ 생성기의 이전 버전입니다.

이런 방식으로 알고리즘을 조정하면 ‘앞서 살펴보기’를 시도하는 불필요한 복잡성을 제외하고 현재 보유하고있는 것과 동일한 것을 생성 할 수 있습니다.

앞을 들여다 보는 것은 실수이며 그렇게해서는 안됩니다.