Pyton/バイトコード実行を読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[Pythonを読む]]
#contents
*_PyEval_EvalCodeWithName (Python/ceval.c) [#rb987115]
バイトコードができたのでいよいよ実行です。バイトコードの...
さて、_PyEval_EvalCodeWithNameですが大部分は引数の処理を...
#code(C){{
static PyObject *
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals...
PyObject **args, int argcount, PyObject **kws,...
PyObject **defs, int defcount, PyObject *kwdef...
PyObject *name, PyObject *qualname)
{
PyCodeObject* co = (PyCodeObject*)_co;
PyFrameObject *f;
PyObject *retval = NULL;
PyThreadState *tstate = PyThreadState_GET();
f = PyFrame_New(tstate, co, globals, locals);
// 引数処理
retval = PyEval_EvalFrameEx(f,0);
fail: /* Jump here from prelude on failure */
/* decref'ing the frame can cause __del__ methods to ...
which can call back into Python. While we're done...
current Python frame (f), the associated C stack i...
so recursion_depth must be boosted for the duration.
*/
++tstate->recursion_depth;
Py_DECREF(f);
--tstate->recursion_depth;
return retval;
}
}}
実行オブジェクトに対してフレームを割り当て、実行していま...
*PyFrame_New (Objects/frameobject.c) [#ha3d9e95]
まずフレーム構造について確認します。PyFrameObjectの定義は...
#code(C){{
typedef struct _frame {
PyObject_VAR_HEAD
struct _frame *f_back; /* previous frame, or NUL...
PyCodeObject *f_code; /* code segment */
PyObject *f_builtins; /* builtin symbol table (...
PyObject *f_globals; /* global symbol table (P...
PyObject *f_locals; /* local symbol table (an...
PyObject **f_valuestack; /* points after the last ...
/* Next free slot in f_valuestack. Frame creation se...
Frame evaluation usually NULLs it, but a frame tha...
to the current stack top. */
PyObject **f_stacktop;
PyObject *f_trace; /* Trace function */
/* In a generator, we need to be able to swap bet...
state inside the generator and the exception s...
frame (which shouldn't be impacted when the ge...
from an except handler).
These three fields exist exactly for that, and...
non-generator frames. See the save_exc_state a...
functions in ceval.c for details of their use....
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
/* Borrowed reference to a generator, or NULL */
PyObject *f_gen;
int f_lasti; /* Last instruction if ca...
int f_lineno; /* Current line number */
int f_iblock; /* index in f_blockstack */
char f_executing; /* whether the frame is s...
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and...
PyObject *f_localsplus[1]; /* locals+stack, dynamica...
} PyFrameObject;
}}
普通のフレーム構造です。少し特殊なのはf_localsplusですね...
ではPyFame_Newを見てみましょう。
#code(C){{
PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, Py...
PyObject *locals)
{
PyFrameObject *back = tstate->frame;
PyFrameObject *f;
PyObject *builtins;
Py_ssize_t i;
// builtinsの処理。省略
if (code->co_zombieframe != NULL) {
f = code->co_zombieframe;
code->co_zombieframe = NULL;
_Py_NewReference((PyObject *)f);
}
else {
Py_ssize_t extras, ncells, nfrees;
ncells = PyTuple_GET_SIZE(code->co_cellvars);
nfrees = PyTuple_GET_SIZE(code->co_freevars);
extras = code->co_stacksize + code->co_nlocals + ...
if (free_list == NULL) {
f = PyObject_GC_NewVar(PyFrameObject, &PyFram...
}
else {
--numfree;
f = free_list;
free_list = free_list->f_back;
if (Py_SIZE(f) < extras) {
PyFrameObject *new_f = PyObject_GC_Resize...
f = new_f;
}
_Py_NewReference((PyObject *)f);
}
f->f_code = code;
extras = code->co_nlocals + ncells + nfrees;
f->f_valuestack = f->f_localsplus + extras;
for (i=0; i<extras; i++)
f->f_localsplus[i] = NULL;
f->f_locals = NULL;
f->f_trace = NULL;
f->f_exc_type = f->f_exc_value = f->f_exc_traceba...
}
f->f_stacktop = f->f_valuestack;
f->f_builtins = builtins;
Py_XINCREF(back);
f->f_back = back;
Py_INCREF(code);
Py_INCREF(globals);
f->f_globals = globals;
/* Most functions have CO_NEWLOCALS and CO_OPTIMIZED ...
if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) ...
; /* f_locals = NULL; will be set by PyFrame_Fast...
else if (code->co_flags & CO_NEWLOCALS) {
locals = PyDict_New();
f->f_locals = locals;
}
else {
if (locals == NULL)
locals = globals;
Py_INCREF(locals);
f->f_locals = locals;
}
f->f_lasti = -1;
f->f_lineno = code->co_firstlineno;
f->f_iblock = 0;
f->f_executing = 0;
f->f_gen = NULL;
_PyObject_GC_TRACK(f);
return f;
}
}}
エラー処理とかはさっくり省いて、効率アップのためにフレー...
+前にそのCodeObjectを実行したフレーム(zombieframe)があ...
+free_listにフレームがあればそれを使う(スタックが必要と...
+zombieもfree_listもなければ生成を行う
PyObject_GC_NewVarはややこしいのですが、これはInclude/obj...
#code(C){{
PyVarObject *
_PyObject_GC_NewVar(PyTypeObject *tp, Py_ssize_t nitems)
{
size_t size;
PyVarObject *op;
size = _PyObject_VAR_SIZE(tp, nitems);
op = (PyVarObject *) _PyObject_GC_Malloc(size);
if (op != NULL)
op = PyObject_INIT_VAR(op, tp, nitems);
return op;
}
}}
_PyObject_VAR_SIZEはInclude/objimpl.hにあります。
#code(C){{
#define _PyObject_VAR_SIZE(typeobj, nitems) \
_Py_SIZE_ROUND_UP((typeobj)->tp_basicsize + \
(nitems)*(typeobj)->tp_itemsize, \
SIZEOF_VOID_P)
}}
TypeObjectについては詳しく見ませんがPyFrame_Typeの場合以...
#code(C){{
PyTypeObject PyFrame_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"frame",
sizeof(PyFrameObject),
sizeof(PyObject *),
// 省略
};
}}
sizeof(PyFrameObject)がtp_basicsize、sizeof(PyObject *)が...
*PyEval_EvalFrameEx (Python/ceval.c) [#n68eb0c6]
フレームについて確認できたのでいよいよコードの実行です。
さて、実行方法ですが、よくあるダイレクトスレッデッドコー...
-各命令に対応するswitchのcaseを用意し、ループをひたすら回す
-でもそれだとループする分遅くなってしまうので、次の命令に...
というものです。PyEval_EvalFrameExの上の方にその説明およ...
後、ここまで見てきて想像はつくと思いますがPythonのVMはス...
**LOAD_CONST [#z9c0f515]
LOAD_CONSTは定数をスタックにプッシュしています。ここでい...
**LOAD_FAST [#jc5374df]
LOAD_FASTはローカル変数をスタックにプッシュしています。た...
**LOAD_NAME [#g2006c6b]
名前をローカル変数→グローバル変数→ビルトインの順番で検索...
**STORE_FAST [#n609456a]
STORE_FASTはLOAD_FASTの逆、スタックのトップをローカル変数...
**BINARY_MULTIPLY [#i35218a3]
スタックの一つ目と二つ目を取ってきて掛け算して結果をスタ...
**COMPARE_OP [#t45fa88b]
スタックの一つ目と二つ目の比較処理。どのような比較を行う...
**BUILD_LIST [#d04a7aff]
リストを作成しスタックにプッシュしています。
#code(C){{
TARGET(BUILD_LIST) {
PyObject *list = PyList_New(oparg);
if (list == NULL)
goto error;
while (--oparg >= 0) {
PyObject *item = POP();
PyList_SET_ITEM(list, oparg, item);
}
PUSH(list);
DISPATCH();
}
}}
引数で指定された分スタックからポップし詰めていますが今回...
**LIST_APPEND [#k524ee38]
引数で指定した位置にあるリストにスタックトップの要素を追...
#code(C){{
TARGET(LIST_APPEND) {
PyObject *v = POP();
PyObject *list = PEEK(oparg);
int err;
err = PyList_Append(list, v);
Py_DECREF(v);
if (err != 0)
goto error;
PREDICT(JUMP_ABSOLUTE);
DISPATCH();
}
}}
コード生成の際にLIST_APPENDでは2が指定されていました。つ...
**POP_JUMP_IF_FALSE [#z9d12ced]
スタックトップをポップしてFalseだったら指定されたラベル(...
**JUMP_ABSOLUTE [#bf613da2]
説明する必要もないような絶対ジャンプです。
**MAKE_FUNCTION [#n5f0dcad]
クロージャの処理とかしていますが今回は関係ないのでさっく...
#code(C){{
TARGET(MAKE_FUNCTION)
_make_function: {
PyObject *qualname = POP(); /* qualname */
PyObject *code = POP(); /* code object */
PyObject *func = PyFunction_NewWithQualName(code, f->...
Py_DECREF(code);
Py_DECREF(qualname);
PUSH(func);
DISPATCH();
}
}}
使われている箇所は以下のような感じ。関数を作り、それをス...
LOAD_CONST 内包表記のコード
LOAD_CONST <listcomp>
MAKE_FUNCTION 0
**CALL_FUNCTION [#f3eec3d6]
関数呼び出しです。(そのまんま)
#code(C){{
TARGET(CALL_FUNCTION) {
PyObject **sp, *res;
sp = stack_pointer;
res = call_function(&sp, oparg);
stack_pointer = sp;
PUSH(res);
if (res == NULL)
goto error;
DISPATCH();
}
}}
call_functionではCで実装された関数なのかPythonで書かれた...
なお、呼び出す関数や関数の引数はスタックに積まれています...
LOAD_NAME 0(range)
LOAD_CONST 10
CALL_FUNCTION 1
念のため確認すると、
10
range
というように引数が上、呼び出す関数が下に積まれています。
rangeの場合、関数のように見えますが実体はTypeObjectなので...
PyObject_CallはObjects/abstract.cにあります。TypeObjectの...
さて、rangeの実装はObjects/rangeobject.cにあります。その...
ちょっと横道にそれますが、ここら辺の処理は初めに深く追う...
というわけで、type_callが呼び出されるわけですが、ここでtp...
**GET_ITER [#qa43f0e0]
というわけでスタックにrangeオブジェクトが積まれました。生...
#code(C){{
TARGET(GET_ITER) {
/* before: [obj]; after [getiter(obj)] */
PyObject *iterable = TOP();
PyObject *iter = PyObject_GetIter(iterable);
Py_DECREF(iterable);
SET_TOP(iter);
if (iter == NULL)
goto error;
PREDICT(FOR_ITER);
DISPATCH();
}
}}
rangeオブジェクトがrangeオブジェクトのイテレータオブジェ...
**CALL_FUNCTION(Pythonでの実装の場合) [#j4fa6f75]
GET_ITER後、再度CALL_FUNCTIONが実行されています。
GET_ITER
CALL_FUNCTION 1
スタックトップにはイテレータオブジェクト、その下には内包...
さて、内包表記のコードはPythonで書かれているので先ほどと...
**FOR_ITER [#q461df89]
内包表記のコードではFOR_ITERがポイントになります。
#code(C){{
TARGET(FOR_ITER) {
/* before: [iter]; after: [iter, iter()] *or* [] */
PyObject *iter = TOP();
PyObject *next = (*iter->ob_type->tp_iternext)(iter);
if (next != NULL) {
PUSH(next);
PREDICT(STORE_FAST);
PREDICT(UNPACK_SEQUENCE);
DISPATCH();
}
if (PyErr_Occurred()) {
if (!PyErr_ExceptionMatches(PyExc_StopIteration))
goto error;
else if (tstate->c_tracefunc != NULL)
call_exc_trace(tstate->c_tracefunc, tstate->c...
PyErr_Clear();
}
/* iterator ended normally */
STACKADJ(-1);
Py_DECREF(iter);
JUMPBY(oparg);
DISPATCH();
}
}}
FOR_ITERの使われてるところは以下のようになっています。
BUILD_LIST 0
LOAD_FAST 0
:start
FOR_ITER REL:anchor
:
STORE_FAST 0(n)
LOAD_FAST 0(n)
LOAD_FAST 0(n)
BINARY_MULTIPLY
LOAD_CONST 10
COMPARE_OP >
POP_JUMP_IF_FALSE ABS:if_cleanup
:
LOAD_FAST 0(n)
LIST_APPEND 2
:skip
:if_cleanup
JUMP_ABSOLUTE ABS:start
:anchor
RETURN_VALUE
LOAD_FASTで渡された引数(イテレータオブジェクト)をスタッ...
**RETURN_VALUE [#f4af79da]
スタックトップを戻り値とし、PyEval_EvalFrameExを終了しま...
*おわりに [#i25d4b3b]
というわけで、シェルで
#code(Python){{
[n for n in range(10) if n * n > 10]
}}
と打った時にどのようなことが行われるかを見てきました。Rub...
それではみなさんもよいコードリーディングを。
終了行:
[[Pythonを読む]]
#contents
*_PyEval_EvalCodeWithName (Python/ceval.c) [#rb987115]
バイトコードができたのでいよいよ実行です。バイトコードの...
さて、_PyEval_EvalCodeWithNameですが大部分は引数の処理を...
#code(C){{
static PyObject *
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals...
PyObject **args, int argcount, PyObject **kws,...
PyObject **defs, int defcount, PyObject *kwdef...
PyObject *name, PyObject *qualname)
{
PyCodeObject* co = (PyCodeObject*)_co;
PyFrameObject *f;
PyObject *retval = NULL;
PyThreadState *tstate = PyThreadState_GET();
f = PyFrame_New(tstate, co, globals, locals);
// 引数処理
retval = PyEval_EvalFrameEx(f,0);
fail: /* Jump here from prelude on failure */
/* decref'ing the frame can cause __del__ methods to ...
which can call back into Python. While we're done...
current Python frame (f), the associated C stack i...
so recursion_depth must be boosted for the duration.
*/
++tstate->recursion_depth;
Py_DECREF(f);
--tstate->recursion_depth;
return retval;
}
}}
実行オブジェクトに対してフレームを割り当て、実行していま...
*PyFrame_New (Objects/frameobject.c) [#ha3d9e95]
まずフレーム構造について確認します。PyFrameObjectの定義は...
#code(C){{
typedef struct _frame {
PyObject_VAR_HEAD
struct _frame *f_back; /* previous frame, or NUL...
PyCodeObject *f_code; /* code segment */
PyObject *f_builtins; /* builtin symbol table (...
PyObject *f_globals; /* global symbol table (P...
PyObject *f_locals; /* local symbol table (an...
PyObject **f_valuestack; /* points after the last ...
/* Next free slot in f_valuestack. Frame creation se...
Frame evaluation usually NULLs it, but a frame tha...
to the current stack top. */
PyObject **f_stacktop;
PyObject *f_trace; /* Trace function */
/* In a generator, we need to be able to swap bet...
state inside the generator and the exception s...
frame (which shouldn't be impacted when the ge...
from an except handler).
These three fields exist exactly for that, and...
non-generator frames. See the save_exc_state a...
functions in ceval.c for details of their use....
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
/* Borrowed reference to a generator, or NULL */
PyObject *f_gen;
int f_lasti; /* Last instruction if ca...
int f_lineno; /* Current line number */
int f_iblock; /* index in f_blockstack */
char f_executing; /* whether the frame is s...
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and...
PyObject *f_localsplus[1]; /* locals+stack, dynamica...
} PyFrameObject;
}}
普通のフレーム構造です。少し特殊なのはf_localsplusですね...
ではPyFame_Newを見てみましょう。
#code(C){{
PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, Py...
PyObject *locals)
{
PyFrameObject *back = tstate->frame;
PyFrameObject *f;
PyObject *builtins;
Py_ssize_t i;
// builtinsの処理。省略
if (code->co_zombieframe != NULL) {
f = code->co_zombieframe;
code->co_zombieframe = NULL;
_Py_NewReference((PyObject *)f);
}
else {
Py_ssize_t extras, ncells, nfrees;
ncells = PyTuple_GET_SIZE(code->co_cellvars);
nfrees = PyTuple_GET_SIZE(code->co_freevars);
extras = code->co_stacksize + code->co_nlocals + ...
if (free_list == NULL) {
f = PyObject_GC_NewVar(PyFrameObject, &PyFram...
}
else {
--numfree;
f = free_list;
free_list = free_list->f_back;
if (Py_SIZE(f) < extras) {
PyFrameObject *new_f = PyObject_GC_Resize...
f = new_f;
}
_Py_NewReference((PyObject *)f);
}
f->f_code = code;
extras = code->co_nlocals + ncells + nfrees;
f->f_valuestack = f->f_localsplus + extras;
for (i=0; i<extras; i++)
f->f_localsplus[i] = NULL;
f->f_locals = NULL;
f->f_trace = NULL;
f->f_exc_type = f->f_exc_value = f->f_exc_traceba...
}
f->f_stacktop = f->f_valuestack;
f->f_builtins = builtins;
Py_XINCREF(back);
f->f_back = back;
Py_INCREF(code);
Py_INCREF(globals);
f->f_globals = globals;
/* Most functions have CO_NEWLOCALS and CO_OPTIMIZED ...
if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) ...
; /* f_locals = NULL; will be set by PyFrame_Fast...
else if (code->co_flags & CO_NEWLOCALS) {
locals = PyDict_New();
f->f_locals = locals;
}
else {
if (locals == NULL)
locals = globals;
Py_INCREF(locals);
f->f_locals = locals;
}
f->f_lasti = -1;
f->f_lineno = code->co_firstlineno;
f->f_iblock = 0;
f->f_executing = 0;
f->f_gen = NULL;
_PyObject_GC_TRACK(f);
return f;
}
}}
エラー処理とかはさっくり省いて、効率アップのためにフレー...
+前にそのCodeObjectを実行したフレーム(zombieframe)があ...
+free_listにフレームがあればそれを使う(スタックが必要と...
+zombieもfree_listもなければ生成を行う
PyObject_GC_NewVarはややこしいのですが、これはInclude/obj...
#code(C){{
PyVarObject *
_PyObject_GC_NewVar(PyTypeObject *tp, Py_ssize_t nitems)
{
size_t size;
PyVarObject *op;
size = _PyObject_VAR_SIZE(tp, nitems);
op = (PyVarObject *) _PyObject_GC_Malloc(size);
if (op != NULL)
op = PyObject_INIT_VAR(op, tp, nitems);
return op;
}
}}
_PyObject_VAR_SIZEはInclude/objimpl.hにあります。
#code(C){{
#define _PyObject_VAR_SIZE(typeobj, nitems) \
_Py_SIZE_ROUND_UP((typeobj)->tp_basicsize + \
(nitems)*(typeobj)->tp_itemsize, \
SIZEOF_VOID_P)
}}
TypeObjectについては詳しく見ませんがPyFrame_Typeの場合以...
#code(C){{
PyTypeObject PyFrame_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"frame",
sizeof(PyFrameObject),
sizeof(PyObject *),
// 省略
};
}}
sizeof(PyFrameObject)がtp_basicsize、sizeof(PyObject *)が...
*PyEval_EvalFrameEx (Python/ceval.c) [#n68eb0c6]
フレームについて確認できたのでいよいよコードの実行です。
さて、実行方法ですが、よくあるダイレクトスレッデッドコー...
-各命令に対応するswitchのcaseを用意し、ループをひたすら回す
-でもそれだとループする分遅くなってしまうので、次の命令に...
というものです。PyEval_EvalFrameExの上の方にその説明およ...
後、ここまで見てきて想像はつくと思いますがPythonのVMはス...
**LOAD_CONST [#z9c0f515]
LOAD_CONSTは定数をスタックにプッシュしています。ここでい...
**LOAD_FAST [#jc5374df]
LOAD_FASTはローカル変数をスタックにプッシュしています。た...
**LOAD_NAME [#g2006c6b]
名前をローカル変数→グローバル変数→ビルトインの順番で検索...
**STORE_FAST [#n609456a]
STORE_FASTはLOAD_FASTの逆、スタックのトップをローカル変数...
**BINARY_MULTIPLY [#i35218a3]
スタックの一つ目と二つ目を取ってきて掛け算して結果をスタ...
**COMPARE_OP [#t45fa88b]
スタックの一つ目と二つ目の比較処理。どのような比較を行う...
**BUILD_LIST [#d04a7aff]
リストを作成しスタックにプッシュしています。
#code(C){{
TARGET(BUILD_LIST) {
PyObject *list = PyList_New(oparg);
if (list == NULL)
goto error;
while (--oparg >= 0) {
PyObject *item = POP();
PyList_SET_ITEM(list, oparg, item);
}
PUSH(list);
DISPATCH();
}
}}
引数で指定された分スタックからポップし詰めていますが今回...
**LIST_APPEND [#k524ee38]
引数で指定した位置にあるリストにスタックトップの要素を追...
#code(C){{
TARGET(LIST_APPEND) {
PyObject *v = POP();
PyObject *list = PEEK(oparg);
int err;
err = PyList_Append(list, v);
Py_DECREF(v);
if (err != 0)
goto error;
PREDICT(JUMP_ABSOLUTE);
DISPATCH();
}
}}
コード生成の際にLIST_APPENDでは2が指定されていました。つ...
**POP_JUMP_IF_FALSE [#z9d12ced]
スタックトップをポップしてFalseだったら指定されたラベル(...
**JUMP_ABSOLUTE [#bf613da2]
説明する必要もないような絶対ジャンプです。
**MAKE_FUNCTION [#n5f0dcad]
クロージャの処理とかしていますが今回は関係ないのでさっく...
#code(C){{
TARGET(MAKE_FUNCTION)
_make_function: {
PyObject *qualname = POP(); /* qualname */
PyObject *code = POP(); /* code object */
PyObject *func = PyFunction_NewWithQualName(code, f->...
Py_DECREF(code);
Py_DECREF(qualname);
PUSH(func);
DISPATCH();
}
}}
使われている箇所は以下のような感じ。関数を作り、それをス...
LOAD_CONST 内包表記のコード
LOAD_CONST <listcomp>
MAKE_FUNCTION 0
**CALL_FUNCTION [#f3eec3d6]
関数呼び出しです。(そのまんま)
#code(C){{
TARGET(CALL_FUNCTION) {
PyObject **sp, *res;
sp = stack_pointer;
res = call_function(&sp, oparg);
stack_pointer = sp;
PUSH(res);
if (res == NULL)
goto error;
DISPATCH();
}
}}
call_functionではCで実装された関数なのかPythonで書かれた...
なお、呼び出す関数や関数の引数はスタックに積まれています...
LOAD_NAME 0(range)
LOAD_CONST 10
CALL_FUNCTION 1
念のため確認すると、
10
range
というように引数が上、呼び出す関数が下に積まれています。
rangeの場合、関数のように見えますが実体はTypeObjectなので...
PyObject_CallはObjects/abstract.cにあります。TypeObjectの...
さて、rangeの実装はObjects/rangeobject.cにあります。その...
ちょっと横道にそれますが、ここら辺の処理は初めに深く追う...
というわけで、type_callが呼び出されるわけですが、ここでtp...
**GET_ITER [#qa43f0e0]
というわけでスタックにrangeオブジェクトが積まれました。生...
#code(C){{
TARGET(GET_ITER) {
/* before: [obj]; after [getiter(obj)] */
PyObject *iterable = TOP();
PyObject *iter = PyObject_GetIter(iterable);
Py_DECREF(iterable);
SET_TOP(iter);
if (iter == NULL)
goto error;
PREDICT(FOR_ITER);
DISPATCH();
}
}}
rangeオブジェクトがrangeオブジェクトのイテレータオブジェ...
**CALL_FUNCTION(Pythonでの実装の場合) [#j4fa6f75]
GET_ITER後、再度CALL_FUNCTIONが実行されています。
GET_ITER
CALL_FUNCTION 1
スタックトップにはイテレータオブジェクト、その下には内包...
さて、内包表記のコードはPythonで書かれているので先ほどと...
**FOR_ITER [#q461df89]
内包表記のコードではFOR_ITERがポイントになります。
#code(C){{
TARGET(FOR_ITER) {
/* before: [iter]; after: [iter, iter()] *or* [] */
PyObject *iter = TOP();
PyObject *next = (*iter->ob_type->tp_iternext)(iter);
if (next != NULL) {
PUSH(next);
PREDICT(STORE_FAST);
PREDICT(UNPACK_SEQUENCE);
DISPATCH();
}
if (PyErr_Occurred()) {
if (!PyErr_ExceptionMatches(PyExc_StopIteration))
goto error;
else if (tstate->c_tracefunc != NULL)
call_exc_trace(tstate->c_tracefunc, tstate->c...
PyErr_Clear();
}
/* iterator ended normally */
STACKADJ(-1);
Py_DECREF(iter);
JUMPBY(oparg);
DISPATCH();
}
}}
FOR_ITERの使われてるところは以下のようになっています。
BUILD_LIST 0
LOAD_FAST 0
:start
FOR_ITER REL:anchor
:
STORE_FAST 0(n)
LOAD_FAST 0(n)
LOAD_FAST 0(n)
BINARY_MULTIPLY
LOAD_CONST 10
COMPARE_OP >
POP_JUMP_IF_FALSE ABS:if_cleanup
:
LOAD_FAST 0(n)
LIST_APPEND 2
:skip
:if_cleanup
JUMP_ABSOLUTE ABS:start
:anchor
RETURN_VALUE
LOAD_FASTで渡された引数(イテレータオブジェクト)をスタッ...
**RETURN_VALUE [#f4af79da]
スタックトップを戻り値とし、PyEval_EvalFrameExを終了しま...
*おわりに [#i25d4b3b]
というわけで、シェルで
#code(Python){{
[n for n in range(10) if n * n > 10]
}}
と打った時にどのようなことが行われるかを見てきました。Rub...
それではみなさんもよいコードリーディングを。
ページ名: