12

Pythonジェネレーターは、ほとんどの場合、プレーンジェネレーターでは不可能な空の状態をチェックしたい場合にリストの代わりになります。私は空の状態をチェックできるラッパーを書き込もうとしていますが、それでも怠惰でジェネレーターの利点を提供します。

class mygen:
  def __init__(self,iterable):
    self.iterable = (x for x in iterable)
    self.peeked = False
    self.peek = None
  def __iter__(self):
    if self.peeked:
      yield self.peek
      self.peeked = False
    for val in self.iterable:
      if self.peeked:
        yield self.peek
        self.peeked = False
      yield val
    if self.peeked:
      yield self.peek
      self.peeked = False
  def __nonzero__(self):
    if self.peeked:
      return True
    try:
      self.peek = self.iterable.next()
      self.peeked = True
      return True
    except:
      return False
  1. 普通のジェネレーターのように正しく動作すると思います。足りないコーナーケースはありますか?
  2. これはエレガントに見えません。同じことを行うためのより良いよりPython的な方法はありますか?

使用例:

def get_odd(l):
    return mygen(x for x in l if x%2)

def print_odd(odd_nums):
  if odd_nums:
      print "odd numbers found",list(odd_nums)
  else:
      print "No odd numbers found"

print_odd(get_odd([2,4,6,8]))
print_odd(get_odd([2,4,6,8,7]))
4

2 に答える 2

12

私は通常、この種のジェネレーターを実装しません。イテレータit が使い果たされているかどうかをテストする慣用的な方法があります。

try:
    next_item = next(it)
except StopIteration:
    # exhausted, handle this case

このEAFPイディオムをプロジェクト固有のLBYLイディオムに置き換えると、混乱を招き、まったく有益ではないように思われます。

そうは言っても、本当にやりたいのであれば、これをどのように実装するかを次に示します。

class MyIterator(object):
    def __init__(self, iterable):
        self._iterable = iter(iterable)
        self._exhausted = False
        self._cache_next_item()
    def _cache_next_item(self):
        try:
            self._next_item = next(self._iterable)
        except StopIteration:
            self._exhausted = True
    def __iter__(self):
        return self
    def next(self):
        if self._exhausted:
            raise StopIteration
        next_item = self._next_item
        self._cache_next_item()
        return next_item
    def __nonzero__(self):
        return not self._exhausted
于 2012-07-13T09:25:41.443 に答える
4

itertools.teeゼロ以外のテストを実装するために使用し、作成時にキャッシュします。

from itertools import tee

class NonZeroIterable(object):
    def __init__(self, iterable):
        self.__iterable, test = tee(iter(iterable))
        try:
            test.next()
            self.__nonzero = True
        except StopIteration:
            self.__nonzero = False                 

    def __nonzero__(self):
        return self.__nonzero

    def __iter__(self):
        return self.__iterable

小さなデモ:

>>> nz = NonZeroIterable('foobar')
>>> if nz: print list(nz)
... 
['f', 'o', 'o', 'b', 'a', 'r']
>>> nz2 = NonZeroIterable([])
>>> if not nz2: print 'empty'
... 
empty

このバージョンのNonZeroIterableは、フラグをキャッシュします。したがって、イテレータが最初に空でなかったかどうかだけがわかります。ライフサイクルの他の時点でイテラブルをテストできるようにする必要がある場合は、代わりにSvenのバージョンを使用してください。そこに__nonzero__フラグは、まだ来るアイテムがまだあるかどうか、すべての反復の後にあなたに知らせます。

あなたの例に関するサイドノート

サンプルコードは単純すぎて、ユースケースに適した議論ではありません。最初に空でないことをテストしますが(入力リストを反復処理して奇数を検索する可能性があります)、とにかく反復子全体を使い果たします。次のコードも同様に効率的であり、Pythonイディオムを破る方法を考案する必要はありません。

def print_odd(odd_nums):
    odd_nums = list(odd_nums)
    if odd_nums:
        print "odd numbers found", odd_nums
    else:
        print "No odd numbers found"
于 2012-07-13T09:26:01.490 に答える