楽しみのために、アーロンの提案に基づいて先読みクラスの実装を作成しました。
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 まで)。