yield ステートメントを含むすべての関数は、ジェネレーター オブジェクトを返します。
正解です。a を含む関数の戻り値はyield
ジェネレータ オブジェクトです。ジェネレーター オブジェクトは反復子であり、各反復はyield
、ジェネレーターをサポートするコードから編集された値を返します。
ジェネレーターオブジェクトは、状態を含むスタックです
ジェネレーター オブジェクトには、現在の実行フレームへのポインターと、ジェネレーターの状態を維持するために使用されるその他のものがすべて含まれています。実行フレームは、ジェネレータ内のコードのコール スタックを含むものです。
.next メソッドを呼び出すたびに、Python は関数の状態を抽出し、別の yield ステートメントを見つけると、状態を再度バインドして前の状態を削除します
並べ替え。を呼び出すとnext(gen_object)
、Pythonは現在の実行フレームを評価します。
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) { // This is called when you call next(gen_object)
PyFrameObject *f = gen->gi_frame;
...
gen->gi_running = 1;
result = PyEval_EvalFrameEx(f, exc); // This evaluates the current frame
gen->gi_running = 0;
PyEval_EvalFrame
Python バイトコードを解釈するために使用される最高レベルの関数です。
PyObject* PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
これは、Python 解釈の主な未加工の関数です。文字通り2000行の長さです。実行フレーム f に関連付けられたコード オブジェクトが実行され、必要に応じてバイトコードが解釈され、呼び出しが実行されます。追加の throwflag パラメータはほとんど無視できます。true の場合、すぐに例外がスローされます。これは、ジェネレータ オブジェクトの throw() メソッドに使用されます。
yield
バイトコードの評価中にa にヒットすると、呼び出し元に渡された値を返す必要があることを認識しています。
TARGET(YIELD_VALUE) {
retval = POP();
f->f_stacktop = stack_pointer;
why = WHY_YIELD;
goto fast_yield;
}
譲ると、フレームの値スタックの現在の値が ( 経由で) 維持されるため、が再び呼び出されたf->f_stacktop = stack_pointer
ときに中断したところから再開できます。next
すべての非ジェネレーター関数は、評価が完了した後に に設定されますf_stacktop
。そのため、ジェネレーター オブジェクトを再度NULL
呼び出すと、以前と同じフレーム ポインターを使用して、 が再度呼び出されます。ポインターの状態は、前に降伏したときとまったく同じになるため、その時点から実行が続行されます。基本的に、フレームの現在の状態は「凍結」です。これは、ジェネレーターを導入した PEP で説明されています。next
PyEval_ExvalFrameEx
yield ステートメントが検出された場合、関数の状態は凍結され、値 [yielded] が .next() の呼び出し元に返されます。「凍結」とは、ローカル変数の現在のバインディング、命令ポインター、および内部評価スタックを含む、すべてのローカル状態が保持されることを意味します。十分な情報が保存されるため、次に .next() が呼び出されたときに、関数は次のことができます。 yield ステートメントが単なる別の外部呼び出しであるかのように処理します。
ジェネレーター オブジェクトが保持する状態のほとんどを次に示します (ヘッダー ファイルから直接取得)。
typedef struct {
PyObject_HEAD
/* The gi_ prefix is intended to remind of generator-iterator. */
/* Note: gi_frame can be NULL if the generator is "finished" */
struct _frame *gi_frame;
/* True if generator is being executed. */
char gi_running;
/* The code object backing the generator */
PyObject *gi_code;
/* List of weak reference. */
PyObject *gi_weakreflist;
/* Name of the generator. */
PyObject *gi_name;
/* Qualified name of the generator. */
PyObject *gi_qualname;
} PyGenObject;
gi_frame
現在の実行フレームへのポインタです。
これはすべて CPython の実装固有であることに注意してください。PyPy/Jython/など。まったく異なる方法でジェネレーターを実装している可能性が非常に高いです。ジェネレーター オブジェクトのソースを読んで、 CPython の実装についてさらに学ぶことをお勧めします。