我看到這些文章:
這篇文章告訴我該怎麼做PyEval_EvalFrameEx工作。
http://tech.blog.aknin.name/2010/09/02/pythons-innards-hello-ceval-c-2/
這篇文章告訴我,在Python框架結構。
http://tech.blog.aknin.name/2010/07/22/pythons-innards-interpreter-stacks/
這兩個東西是對我們非常重要。
那麼讓我自己回答我的問題。我不知道我是否正確。
如果我有誤解或完全錯誤,請讓我知道。
如果我有代碼:
def gen():
count = 0
while count < 10:
count += 1
print 'call here'
yield count
這是一個非常簡單的發電機。
f = gen()
而且每次我們調用它時,Python都會創建一個生成器對象。
PyObject *
PyGen_New(PyFrameObject *f)
{
PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type);
if (gen == NULL) {
Py_DECREF(f);
return NULL;
}
gen->gi_frame = f;
Py_INCREF(f->f_code);
gen->gi_code = (PyObject *)(f->f_code);
gen->gi_running = 0;
gen->gi_weakreflist = NULL;
_PyObject_GC_TRACK(gen);
return (PyObject *)gen;
}
我們可以看到它init是一個生成器對象。並初始化一個Frame
。
什麼我們不喜歡f.send()
或f.next()
,它會調用gen_send_ex
,和下面的代碼:功能
static PyObject *
gen_iternext(PyGenObject *gen)
{
return gen_send_ex(gen, NULL, 0);
}
static PyObject *
gen_send(PyGenObject *gen, PyObject *arg)
{
return gen_send_ex(gen, arg, 0);
}
唯一的區別之間二是阿根廷,發送被髮送ARG,未來派NULL。
gen_send_ex下面的代碼:
static PyObject *
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc)
{
PyThreadState *tstate = PyThreadState_GET();
PyFrameObject *f = gen->gi_frame;
PyObject *result;
if (gen->gi_running) {
fprintf(stderr, "gi init\n");
PyErr_SetString(PyExc_ValueError,
"generator already executing");
return NULL;
}
if (f==NULL || f->f_stacktop == NULL) {
fprintf(stderr, "check stack\n");
/* Only set exception if called from send() */
if (arg && !exc)
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
if (f->f_lasti == -1) {
fprintf(stderr, "f->f_lasti\n");
if (arg && arg != Py_None) {
fprintf(stderr, "something here\n");
PyErr_SetString(PyExc_TypeError,
"can't send non-None value to a "
"just-started generator");
return NULL;
}
} else {
/* Push arg onto the frame's value stack */
fprintf(stderr, "frame\n");
if(arg) {
/* fprintf arg */
}
result = arg ? arg : Py_None;
Py_INCREF(result);
*(f->f_stacktop++) = result;
}
fprintf(stderr, "here\n");
/* Generators always return to their most recent caller, not
* necessarily their creator. */
Py_XINCREF(tstate->frame);
assert(f->f_back == NULL);
f->f_back = tstate->frame;
gen->gi_running = 1;
result = PyEval_EvalFrameEx(f, exc);
gen->gi_running = 0;
/* Don't keep the reference to f_back any longer than necessary. It
* may keep a chain of frames alive or it could create a reference
* cycle. */
assert(f->f_back == tstate->frame);
Py_CLEAR(f->f_back);
/* If the generator just returned (as opposed to yielding), signal
* that the generator is exhausted. */
if (result == Py_None && f->f_stacktop == NULL) {
fprintf(stderr, "here2\n");
Py_DECREF(result);
result = NULL;
/* Set exception if not called by gen_iternext() */
if (arg)
PyErr_SetNone(PyExc_StopIteration);
}
if (!result || f->f_stacktop == NULL) {
fprintf(stderr, "here3\n");
/* generator can't be rerun, so release the frame */
Py_DECREF(f);
gen->gi_frame = NULL;
}
fprintf(stderr, "return result\n");
return result;
}
貌似發電機對象是它自己的框架,其被稱爲gi_frame的控制器。
我添加一些fprintf中(...),所以讓我們運行代碼。
f.next()
f->f_lasti
here
call here
return result
1
所以,首先它去f_lasti
(這是一個整數偏移進入最後執行的指令字節碼,初始化爲-1),是的,它是-1,但無參數,然後去功能上。
,則跳轉here
,最重要的是現在PyEval_EvalFrameEx。 PyEval_EvalFrameEx實現CPython的的評價循環,我們可以事它運行的每個代碼(其實是Python的操作碼),並運行線print 'call here'
,其打印文本。
當代碼去yield
,Python的使用框架對象存儲上下文(我們可以搜索調用堆棧)。回饋價值並放棄對代碼的控制。
一切完成之後,然後return result
,並示出了終端值1
。
下一次,我們接下來的運行(),它不會去f_lasti
範圍。它顯示:
frame
here
call here
return result
2
我們沒有送ARG所以還是從PyEval_EvalFrameEx得到結果和結果是2
爲#3,當然,這是一個不好的做法,使用一次性事件迭代器協議一個簡單的'return'就足夠了。 –
@PauloScardine謝謝! – GuoJing
http://cn.slideshare.net/dabeaz/generators-the-final-frontier –