質問
はありませる簡単な方法の一つの試験の場合は発生装置は該当事項はありませんのように、peek、hasNext,isEmptyかされるようになってい?
解決
あなたの質問に簡単な答え:いいえ、簡単な方法がありません。回避策の全体の多くがあります。
のメモリにシーケンスを保持せずに出力への道値のシーケンスは、<全角>:本当にための発電機が何であるかを、簡単な方法があるべきではありません。だから、何の後方トラバーサルはありません。
あなたはhas_next関数を書いたり、多分あなたがしたい場合は派手なデコレータとメソッドとして発電機にそれを平手打ちことができます。
他のヒント
提案ます:
def peek(iterable):
try:
first = next(iterable)
except StopIteration:
return None
return first, itertools.chain([first], iterable)
使用方法:
res = peek(mysequence)
if res is None:
# sequence is empty. Do stuff.
else:
first, mysequence = res
# Do something with first, maybe?
# Then iterate over the sequence:
for element in mysequence:
# etc.
簡単な方法は、次()のためのオプションのパラメータを使用することです>発電機が消耗(または空)である場合に使用されます。たとえばます:
iterable = some_generator()
_exhausted = object()
if next(iterable, _exhausted) == _exhausted:
print('generator is empty')
は編集:問題を修正しmehtunguhさんのコメントで指摘の
。最善のアプローチ、私見では、特別なテストを避けることであろう。ほとんどの時間、発電機の使用のはのテスト:
thing_generated = False
# Nothing is lost here. if nothing is generated,
# the for block is not executed. Often, that's the only check
# you need to do. This can be done in the course of doing
# the work you wanted to do anyway on the generated output.
for thing in my_generator():
thing_generated = True
do_work(thing)
それが十分でない場合は、、あなたはまだ明示的なテストを実行することができます。この時点で、thing
生成された最後の値が含まれます。何も発生しなかった場合、それは定義されません - あなたはすでに変数を定義した場合を除きます。あなたは<=>の値をチェックすることもできますが、それは少し信頼できないのです。代わりに、単にブロック内でフラグを設定し、その後、それをチェックします:
if not thing_generated:
print "Avast, ye scurvy dog!"
next(generator, None) is not None
交換 None
な価値を知っています ない に機能します。
編集:あり、スキップ1アイテムは機能します。また、いかどうかをチェックの発電機が空のみの検証をその目的範囲を超えて利用んですね。その他いようなもの:
def foo(self):
if next(self.my_generator(), None) is None:
raise Exception("Not initiated")
for x in self.my_generator():
...
すなわち、この作品の場合 発電機 から 機能, して、 generator()
.
私はあなた絶対にのHAD のは、これを実行すると、他の回答のように、発電機を消費しないように場合、私は、自分自身を使用しますが、ではないでしょう、特に1、第二の溶液を提供することを嫌い:
def do_something_with_item(item):
print item
empty_marker = object()
try:
first_item = my_generator.next()
except StopIteration:
print 'The generator was empty'
first_item = empty_marker
if first_item is not empty_marker:
do_something_with_item(first_item)
for item in my_generator:
do_something_with_item(item)
今、私は、これは発電機が使用される方法ではないと信じているので、私は本当に、このソリューションを好きではありません。
申し訳ありませんが、やることであろう最善の方法:
for item in my_generator:
print item
さて、あなたはそれを使用している間、発電機が空であることを検出しました。発電機が空である場合はもちろん、項目が表示されません。
これはまさにあなたのコードで合わないかもしれないが、これは発電機のイディオムが何のためにあるのかです:反復なので、おそらくあなたは少しあなたのアプローチを変えるかもしれない、またはまったくの発電機を使用しない
。私はこのポストは、この時点で5歳であることを認識が、私はこれを行うための慣用的な方法を探している間にそれを見つけて、私の解決策が掲載表示されませんでした。だから、後世のために:
import itertools
def get_generator():
"""
Returns (bool, generator) where bool is true iff the generator is not empty.
"""
gen = (i for i in [0, 1, 2, 3, 4])
a, b = itertools.tee(gen)
try:
a.next()
except StopIteration:
return (False, b)
return (True, b)
私は、多くの論者が指摘すると確信しているとしてもちろん、これはハックであり、唯一の(発電機は、例えば、無料の副作用である)特定の限られた状況では全く動作します。 YMMVます。
あなたは発電機が空であるかどうかを確認するためにやらなければならないことは、次の結果を取得しようとすることです。あなたがいないのであればもちろん、の準備の後、あなたは後で再びそれを返すためにそれを格納する必要があり、その結果を使用しています。
ここで発電機は、簡単な__nonzero__
と空であるかどうかを確認することができますので、if
テストを追加し、既存のイテレータに追加することができますラッパークラスです。それはおそらくデコレータに変換することができます。
class GenWrapper:
def __init__(self, iter):
self.source = iter
self.stored = False
def __iter__(self):
return self
def __nonzero__(self):
if self.stored:
return True
try:
self.value = next(self.source)
self.stored = True
except StopIteration:
return False
return True
def __next__(self): # use "next" (without underscores) for Python 2.x
if self.stored:
self.stored = False
return self.value
return next(self.source)
ここでは、あなたがそれを使用したい方法は次のとおりです。
with open(filename, 'r') as f:
f = GenWrapper(f)
if f:
print 'Not empty'
else:
print 'Empty'
あなたがいないだけで、反復の開始時に、いつでも空虚をチェックすることができることに注意してください。
>>> gen = (i for i in [])
>>> next(gen)
Traceback (most recent call last):
File "<pyshell#43>", line 1, in <module>
next(gen)
StopIteration
末の発生装置 StopIteration
を上げておる場合は、すぐに例外が発生する。 しかし、通常、なんかをチェックの存在の値です。
もうひとつできないとす:
>>> gen = (i for i in [])
>>> if not list(gen):
print('empty generator')
私の場合、私はそれを渡さ前に発電機のホストがzip(...)
、すなわち、アイテムをマージ機能に移入されたかどうかを知るために必要な。解決策は受け入れ答えから、十分に似ているが、異なっています:
定義:
def has_items(iterable):
try:
return True, itertools.chain([next(iterable)], iterable)
except StopIteration:
return False, []
使用方法:
def filter_empty(iterables):
for iterable in iterables:
itr_has_items, iterable = has_items(iterable)
if itr_has_items:
yield iterable
def merge_iterables(iterables):
populated_iterables = filter_empty(iterables)
for items in zip(*populated_iterables):
# Use items for each "slice"
私の特定の問題がイテレート可能オブジェクトが空またはエントリのとまったく同じ数を持っているのどちらかである性質を持っています。
は、ちょうどこのスレッドに落ちたと答えを読むことが非常にシンプルかつ簡単に欠けていたことに気づきます:
def is_empty(generator):
for item in generator:
return False
return True
私たちは、その後、我々は発電機への再注入の最初の項目に必要な任意のアイテムを消費すると仮定されていない場合:
def is_empty_no_side_effects(generator):
try:
item = next(generator)
def my_generator():
yield item
yield from generator
return my_generator(), False
except StopIteration:
return (_ for _ in []), True
例:
>>> g=(i for i in [])
>>> g,empty=is_empty_no_side_effects(g)
>>> empty
True
>>> g=(i for i in range(10))
>>> g,empty=is_empty_no_side_effects(g)
>>> empty
False
>>> list(g)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
あなたは発電機を使用し、その後、いや、簡単な方法はありません。
のあなたは発電機を使用した後、は、簡単な方法があるまで、あなたは待つことができる場合was_empty = True
for some_item in some_generator:
was_empty = False
do_something_with(some_item)
if was_empty:
handle_already_empty_generator_case()
ここで何かを得た場合、私はチェックしながら反復子を返すに保つために使用し、私の単純なアプローチであります ループが実行されている場合、私はちょうどチェックします:
n = 0
for key, value in iterator:
n+=1
yield key, value
if n == 0:
print ("nothing found in iterator)
break
ここで発電機を包むシンプルなデコレータですので、空の場合にはNoneを返します。あなたのコードは、発電機が何を生み出すかどうかを知る必要がある場合、これは便利です。<全角> の前にそれをループます。
def generator_or_none(func):
"""Wrap a generator function, returning None if it's empty. """
def inner(*args, **kwargs):
# peek at the first item; return None if it doesn't exist
try:
next(func(*args, **kwargs))
except StopIteration:
return None
# return original generator otherwise first item will be missing
return func(*args, **kwargs)
return inner
使用方法:
import random
@generator_or_none
def random_length_generator():
for i in range(random.randint(0, 10)):
yield i
gen = random_length_generator()
if gen is None:
print('Generator is empty')
これは有用である一例では、テンプレートコードである - すなわちJinja2の
{% if content_generator %}
<section>
<h4>Section title</h4>
{% for item in content_generator %}
{{ item }}
{% endfor %
</section>
{% endif %}
単に、 itertools.chain にして発電機を包む置きます単純にそれをチェックし、その後、第二反復可能として反復可能なの終わりを表します何かます。
例:
import itertools
g = some_iterable
eog = object()
wrap_g = itertools.chain(g, [eog])
今残っているすべてはあなたが
終了を意味することを、それを読んだとき、私たちは反復可能なの最後に追加その値をチェックすることですfor value in wrap_g:
if value == eog: # DING DING! We just found the last element of the iterable
pass # Do something
あなたはそれが空の場合発見する最初の反復までのチェックのみ必要islice使用します。
itertools輸入isliceから
のisEmpty(反復可能)DEF:
リストを返す(islice(反復可能な、1))== []
使用についてどのような任意の()?私は発電機とそれを使用して、それが正常に動作しています。 ここについて少し説明する男がありますこの
cytoolzに PEEK の機能を使用してください。
from cytoolz import peek
from typing import Tuple, Iterable
def is_empty_iterator(g: Iterable) -> Tuple[Iterable, bool]:
try:
_, g = peek(g)
return g, False
except StopIteration:
return g, True
この関数によって返される反復子は、引数として渡された元のものと同等になります。
マーク身代金に促され、ここにあなたが戻ってストリームに、先にプッシュ値をのぞくと、空をチェックできるように、任意のイテレータをラップするために使用できるクラスです。それは私が過去に非常に便利見つけたという単純な実装にシンプルなアイデアだ。
class Pushable:
def __init__(self, iter):
self.source = iter
self.stored = []
def __iter__(self):
return self
def __bool__(self):
if self.stored:
return True
try:
self.stored.append(next(self.source))
except StopIteration:
return False
return True
def push(self, value):
self.stored.append(value)
def peek(self):
if self.stored:
return self.stored[-1]
value = next(self.source)
self.stored.append(value)
return value
def __next__(self):
if self.stored:
return self.stored.pop()
return next(self.source)
私は、SUM関数を使用して、それを解決しました。私は(ジェネレータを返す)glob.iglobと共に使用例については以下を参照。
def isEmpty():
files = glob.iglob(search)
if sum(1 for _ in files):
return True
return False
*これはおそらく、巨大な発電機では動作しませんが、小さいリストのためにうまく実行する必要があります。