2012-03-15 8 views
2

私が使用するライブラリの多くの機能は、結果が何らかのコレクションであるときにlistの代わりにgeneratorを返します。ジェネレータが本当に1つの行に何かを生成するかどうかを確認します。

時々私はちょうど結果ではないの空であるかどうかを確認したい、そしてもちろん私のような何か書くことはできません。今、私はせずにこの問題を解決するワンライナーを思い付く

result = return_generator() 
    if result: 
     print 'Yes, the generator did generate something!' 

を発電機を消費:1行でこの問題を解決するためのクリーンな方法があるかどう

result = return_generator() 
    if zip("_", result): 
     print 'Yes, the generator did generate something!' 

かしら?

+0

* sigh *今日は本当の脳のフェードがあります。私はこの質問に適任者を熱望しています。 :P – detly

+0

@detlyいいえ、発電機を消費しません。降伏してテストするときに印刷するジェネレータを書くことができます。 – satoru

+0

ええ、私はそれをテストしたときに気付いた(それは最初のアイテムを消費するものの)。だから私は元のコメントを削除したのです:P – detly

答えて

3

このzipは、最初に出されたアイテムを食べるので、それは良い考えではありません。

ジェネレータは、必要なときに取得して保持することによって、降伏するアイテムを検出できます。次のクラスは、あなたがそうするのを助けるでしょう。

必要に応じて、イテレータから項目を取得して保持します。

空白を要求された場合(if myiterwatch: ...)、取得できるかどうかを取得して返します。

次の項目について質問された場合、取得した項目または新しい項目が戻されます。

class IterWatch(object): 
    def __init__(self, it): 
     self.iter = iter(it) 
     self._pending = [] 
    @property 
    def pending(self): 
     try: 
      if not self._pending: 
       # will raise StopIteration if exhausted 
       self._pending.append(next(self.iter)) 
     except StopIteration: 
      pass # swallow this 
     return self._pending 
    def next(self): 
     try: 
      return self.pending.pop(0) 
     except IndexError: 
      raise StopIteration 
    __next__ = next # for Py3 
    def __iter__(self): return self 
    def __nonzero__(self): 
     # returns True if we have data. 
     return not not self.pending 
     # or maybe bool(self.pending) 
    __bool__ = __nonzero__ # for Py3 

これは非常に一般的な方法で問題を解決します。あなたはちょうどテストするイテレータを持っている場合は、イテレータaが消耗されている場合、あなたは

guard = object() 
result = return_generator() 
if next(result, guard) is not guard: 
    print 'Yes, the generator did generate something!' 

next(a, b)戻りbを使用することができます。だから私たちの場合にガードを返すなら、それは何かを生成しなかった、そうでなければそれをした。

しかし、あなたのzip()アプローチは、同様に完全に有効である...ここ

+0

はい、後で発電機を使用したいという条件を考慮して、消費された「最初の要素」を復元するために何かをしなければなりません。しかし、私はこれが本当にまれだと思います。なぜなら、この場合は、事前にチェックしなくても発電機を反復するだけなのでです。 – satoru

+0

'next'の2番目のパラメータを教えていただきありがとうございます:) – satoru

2

ジェネレータを返す代わりに、それを使用しますか?結果を返すかどうかを示すジェネレータの例を示します。結果がある場合はアクションが実行され、結果がない場合はアクションは実行されません。

#!/usr/bin/python 

import time 

def do_work(): 
    if int(time.time()) % 2: 
     for a in xrange(0,100): 
      yield a 

for thing in do_work(): 
    print thing 
+0

通常これはうまくいくかもしれませんが、これがうまくいかない場合があります。 – glglgl

+1

まあ、私は私の座席の端にいます。どのような状況ですか? – synthesizerpatel

+0

@glglgl:? (それはあなたの既存の答えを精緻化する価値があるかもしれません。) –

4

は、発電機を複製itertoolsティー機能を使用して、一つの方法である:ここでは

from itertools import tee 
a, b = tee(xrange(0)) 
try: 
    next(a) 
    print list(b) 
except StopIteration: 
    print "f1 was empty" 


a, b = tee(xrange(3)) 
try: 
    next(a) 
    print list(b) 
except StopIteration: 
    print "f2 was empty" 

>>> 
[0, 1, 2, 3] 
f2 was empty 
+1

+1私に何かを教えてください。 'itertools'ができないものはありますか? –

+1

ありがとうございます。イテラブル(aまたはb)のいずれかが他のイテラブルよりもずっと前にある場合、その生成されたすべての値は、後で取得するための他のイテレータのリストに格納する必要があることに注意してください。そのため、このソリューションでは多くのメモリが使用される可能性があります。私はより良い解決策を提案しました。 –

+0

def f(n):return(iは範囲(n)内にある) '、' tee(f(10)) '、' tee(f(0)) 'は代替になります – glglgl

1

は、あなたの状況には2つの最も簡単な解決策があります私が考えることができるのは:

def f1(): 
    return (i for i in range(10)) 

def f2(): 
    return (i for i in range(0)) 


def has_next(g): 
    try: 
     from itertools import chain 
     return chain([g.next()],g) 
    except StopIteration: 
     return False 

g = has_next(f1()) 
if g: 
    print list(g) 

g = has_next(f2()) 
if g: 
    print list(g) 


def has_next(g): 
    for i in g: 
     return chain([i],g) 
    return False 

g = has_next(f1()) 
if g: 
    print list(g) 

g = has_next(f2()) 
if g: 
    print list(g) 
+0

' for i in g:リターンチェーン([i]、g) '私は理解するのにしばらくかかりました。しかしそれは大丈夫です:-) – glglgl

関連する問題