web-dev-qa-db-ja.com

ジェネレーターが起動したばかりかどうかを確認するにはどうすればよいですか?

関数is_just_started、これは次のように動作します。

>>> def gen(): yield 0; yield 1
>>> a = gen()
>>> is_just_started(a) 
True
>>> next(a)
0
>>> is_just_started(a) 
False
>>> next(a)
1
>>> is_just_started(a) 
False
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> is_just_started(a)
False

この機能を実装するにはどうすればよいですか?

.gi_running属性ですが、他の目的で使用されているようです。

ジェネレーターに送信する必要がある最初の値がわかっている場合は、次のようなことができます。

def safe_send(gen, a):
    try:
        return gen.send(a)
    except TypeError as e:
        if "just-started" in e.args[0]:
            gen.send(None)
            return gen.send(a)
        else:
            raise

しかし、これは忌まわしいようです。

49
Claudiu

これはPython 3.2+でのみ機能します:

>>> def gen(): yield 0; yield 1
... 
>>> a = gen()
>>> import inspect
>>> inspect.getgeneratorstate(a)
'GEN_CREATED'
>>> next(a)
0
>>> inspect.getgeneratorstate(a)
'GEN_SUSPENDED'
>>> next(a)
1
>>> inspect.getgeneratorstate(a)
'GEN_SUSPENDED'
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> inspect.getgeneratorstate(a)
'GEN_CLOSED'

したがって、要求された関数は次のとおりです。

import inspect

def is_just_started(gen):
    return inspect.getgeneratorstate(gen) == inspect.GEN_CREATED:

好奇心から、CPythonを調べてこれがどのように決定されているかを調べました...どうやらgenerator.gi_frame.f_lastiこれは、「バイトコードで最後に試行された命令のインデックス」です。もし -1その後、まだ開始されていません。

ここにpy2バージョンがあります:

def is_just_started(gen):
    return gen.gi_frame is not None and gen.gi_frame.f_lasti == -1
69
Tim Tisdall

目的のジェネレーターから単純に生成される新しいジェネレーターを作成します。 最初の値が消費されるとフラグが設定されます。その後、残りのアイテムにyield fromを使用できます。

「is_just_started」状態の監視に関心のあるジェネレーターの代わりに、代替のジェネレーターを使用します。

この手法は非侵入型であり、ソースコードを制御できないジェネレーターでも使用できます。

26
wim

イテレータを作成し、インスタンスプロパティとしてフラグを次のようにイテレータクラスに設定できます。

class gen(object):
    def __init__(self, n):
        self.n = n
        self.num, self.nums = 0, []
        self.is_just_started = True  # Your flag

    def __iter__(self):
        return self

    # Python 3 compatibility
    def __next__(self):
        return self.next()

    def next(self):
        self.is_just_started = False  # Reset flag with next
        if self.num < self.n:
            cur, self.num = self.num, self.num+1
            return cur
        else:
            raise StopIteration()

そして、あなたの値チェック関数は次のようになります:

def is_just_started(my_generator):
    return my_generator.is_just_started

サンプルの実行:

>>> a = gen(2)

>>> is_just_started(a)
True

>>> next(a)
0
>>> is_just_started(a)
False

>>> next(a)
1
>>> is_just_started(a)
False

>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 19, in next
StopIteration

iteratorgeneratorの違いを知るには、- Pythonのジェネレータとイテレータの違い

5