ho visto questi articoli:
questo articolo dirmi come funziona PyEval_EvalFrameEx.
http://tech.blog.aknin.name/2010/09/02/pythons-innards-hello-ceval-c-2/
Questo articolo mi dica la struct telaio in Python.
http://tech.blog.aknin.name/2010/07/22/pythons-innards-interpreter-stacks/
Questi due cose sono molto importanti per noi.
Quindi rispondi alla mia domanda. Non so se ho ragione.
Se ho malinteso o completamente sbagliato, per favore fatemi sapere.
Se Ho codice:
def gen():
count = 0
while count < 10:
count += 1
print 'call here'
yield count
Questo è un generatore molto semplice.
f = gen()
E ogni volta che lo chiamiamo, Python crea un oggetto generatore.
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;
}
Potremmo vederlo avviare un oggetto generatore. E Init a Frame
.
Tutto ciò che ci piace o f.send()
f.next()
, chiamerà gen_send_ex
, e il codice qui sotto:
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);
}
unica differenza tra due funzioni è arg, invia è inviare un'arg, successivo Inviare NULL.
codice gen_send_ex di seguito:
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;
}
Sembra Generator oggetto è un regolatore di essa la propria cornice in cui si chiedeva gi_frame.
Aggiungo un po 'di fprintf (...), quindi eseguiamo il codice.
f.next()
f->f_lasti
here
call here
return result
1
Quindi, prima si va a f_lasti
(Questo è un offset nel bytecode delle ultime istruzioni eseguite intero, inizializzato a -1), e sì è -1, ma senza argomenti, allora la funzione va sopra.
Quindi goto here
, la cosa più importante ora è PyEval_EvalFrameEx. PyEval_EvalFrameEx implementa il ciclo di valutazione di CPython, potremmo far funzionare ogni codice (in effetti è Python opcode) ed eseguire la riga print 'call here'
, stampa testo.
Quando il codice passa a yield
, Python memorizza il contesto utilizzando l'oggetto frame (potremmo cercare Call Stack). Dare valore indietro e rinunciare al controllo del codice.
Dopo aver completato il tutto, quindi return result
e mostrando il valore 1
nel terminale.
La prossima volta che eseguiremo next(), non andrà allo scope f_lasti
. Essa mostra:
frame
here
call here
return result
2
Noi non ha mandato arg in modo ancora ottenere il risultato di PyEval_EvalFrameEx e il risultato è pari a 2.
per 3 #, naturalmente si tratta di una cattiva pratica di utilizzare il protocollo iteratore per una volta gli eventi dove sarebbe sufficiente un semplice 'return'. –
@PauloScardine grazie! – GuoJing
http://cn.slideshare.net/dabeaz/generators-the-final-frontier –