Python/build_class後編
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[Pythonを読む]]
#contents
*はじめに [#l29d1320]
引き続きbuiltin___build_class__を読んでいきます。builtin_...
+メタクラスの決定
+クラス(メソッドなど)を定義するコードを実行し属性辞書を...
+属性辞書を使ってクラスを作成
「クラス定義コード実行(属性辞書の作成)」の部分
#code(C){{
prep = _PyObject_GetAttrId(meta, &PyId___prepare__);
if (prep == NULL) {
// こっちには来ないはず
}
else {
PyObject *pargs[2] = {name, bases};
ns = _PyObject_FastCallDict(prep, pargs, 2, mkw);
Py_DECREF(prep);
}
cell = PyEval_EvalCodeEx(PyFunction_GET_CODE(func), P...
NULL, 0, NULL, 0, NULL, 0, N...
PyFunction_GET_CLOSURE(func));
}}
_PyObject_FastCallDictはCFunctionObjectの場合、_PyCFuncti...
*PyEval_EvalCodeEx (Python/ceval.c) [#a22d3c92]
さて、PyEval_EvalCodeExを見ていきましょう。前にも見ました...
#code(C){{
PyObject *
PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObj...
PyObject **args, int argcount, PyObject **kws,...
PyObject **defs, int defcount, PyObject *kwdef...
{
return _PyEval_EvalCodeWithName(_co, globals, locals,
args, argcount,
kws, kws != NULL ? kw...
kwcount, 2,
defs, defcount,
kwdefs, closure,
NULL, NULL);
}
}}
呼び出しと照らし合わせると、__prepare__により作成された空...
_PyEval_EvalCodeWithNameに移ります。localsの動きを見てみ...
#code(C){{
static PyObject *
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals...
PyObject **args, Py_ssize_t argcount,
PyObject **kwnames, PyObject **kwargs,
Py_ssize_t kwcount, int kwstep,
PyObject **defs, Py_ssize_t defcount,
PyObject *kwdefs, PyObject *closure,
PyObject *name, PyObject *qualname)
{
PyCodeObject* co = (PyCodeObject*)_co;
PyFrameObject *f;
PyObject *retval = NULL;
PyThreadState *tstate;
// 関係ないローカル変数は省略
/* Create the frame */
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 [#k7b9e41a]
裏は取ってないけど意味を考えると最後のelseが実行されるの...
#code(C){{
PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, Py...
PyObject *locals)
{
// 省略
/* 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();
if (locals == NULL) {
Py_DECREF(f);
return NULL;
}
f->f_locals = locals;
}
else {
if (locals == NULL)
locals = globals;
Py_INCREF(locals);
f->f_locals = locals;
}
// 省略
}
}}
ところで、FrameObjectには実はローカル変数と称しているもの...
分けているのは名にし負わばにfast、高速化のためでしょうが...
**STORE_NAME [#t18d1b10]
ここで件のクラス定義コードを見てみましょう。
>>> dis.dis(co.co_consts[0])
2 0 LOAD_NAME 0 (__name__)
2 STORE_NAME 1 (__module__)
4 LOAD_CONST 0 ('Foo')
6 STORE_NAME 2 (__qualname__)
3 8 LOAD_CONST 1 (<code object...
10 LOAD_CONST 2 ('Foo.__init_...
12 MAKE_FUNCTION 0
14 STORE_NAME 3 (__init__)
6 16 LOAD_CONST 3 (<code object...
18 LOAD_CONST 4 ('Foo.method')
20 MAKE_FUNCTION 0
22 STORE_NAME 4 (method)
24 LOAD_CONST 5 (None)
26 RETURN_VALUE
関数のコードオブジェクトと名前の文字列をスタックに積んで...
#code(C){{
TARGET(STORE_NAME) {
PyObject *name = GETITEM(names, oparg);
PyObject *v = POP();
PyObject *ns = f->f_locals;
int err;
if (PyDict_CheckExact(ns))
err = PyDict_SetItem(ns, name, v);
else
err = PyObject_SetItem(ns, name, v);
Py_DECREF(v);
if (err != 0)
goto error;
DISPATCH();
}
}}
というわけで作られた関数オブジェクト(メソッド定義)がloc...
*クラス作成処理 [#u2746556]
builtin___build_class__に戻って残りの処理です。
#code(C){{
if (cell != NULL) {
PyObject *margs[3] = {name, bases, ns};
cls = _PyObject_FastCallDict(meta, margs, 3, mkw);
// 省略
}
error:
Py_XDECREF(cell);
Py_DECREF(ns);
Py_DECREF(meta);
Py_XDECREF(mkw);
Py_DECREF(bases);
return cls;
}
}}
さきほども出てきた_PyObject_FastCallDictですが今回は対象...
#code(C){{
PyObject *
_PyObject_FastCallDict(PyObject *func, PyObject **args, P...
PyObject *kwargs)
{
ternaryfunc call;
PyObject *result = NULL;
if (Py_EnterRecursiveCall(" while calling a Python ob...
return NULL;
}
if (PyFunction_Check(func)) {
result = _PyFunction_FastCallDict(func, args, nar...
}
else if (PyCFunction_Check(func)) {
result = _PyCFunction_FastCallDict(func, args, na...
}
else {
PyObject *tuple;
/* Slow-path: build a temporary tuple */
call = func->ob_type->tp_call;
tuple = _PyStack_AsTuple(args, nargs);
result = (*call)(func, tuple, kwargs);
Py_DECREF(tuple);
result = _Py_CheckFunctionResult(func, result, NU...
}
exit:
Py_LeaveRecursiveCall();
return result;
}
}}
というわけでtp_callとして設定されているtype_callへ。あ、...
**type_call (Objects/typeobject.c) [#gee9a82f]
#code(C){{
type_call(PyTypeObject *type, PyObject *args, PyObject *k...
{
PyObject *obj;
obj = type->tp_new(type, args, kwds);
obj = _Py_CheckFunctionResult((PyObject*)type, obj, N...
if (obj == NULL)
return NULL;
/* Ugly exception: when the call was type(something),
don't call tp_init on the result. */
if (type == &PyType_Type &&
PyTuple_Check(args) && PyTuple_GET_SIZE(args) == ...
(kwds == NULL ||
(PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))
return obj;
/* If the returned object is not an instance of type,
it won't be initialized. */
if (!PyType_IsSubtype(Py_TYPE(obj), type))
return obj;
type = Py_TYPE(obj);
if (type->tp_init != NULL) {
int res = type->tp_init(obj, args, kwds);
if (res < 0) {
assert(PyErr_Occurred());
Py_DECREF(obj);
obj = NULL;
}
else {
assert(!PyErr_Occurred());
}
}
return obj;
}
}}
本筋ではないですが真ん中あたりでUglyと言われている部分に...
この部分はPythonでtype関数を呼び出した時の処理です。type...
**type_new (Objects/typeobject.c) [#id807c38]
tp_newが指しているtype_newへ。type_newは500行近くあるので...
#code(C){{
static PyObject *
type_new(PyTypeObject *metatype, PyObject *args, PyObject...
{
PyObject *name, *bases = NULL, *orig_dict, *dict = NU...
PyObject *qualname, *slots = NULL, *tmp, *newslots, *...
PyTypeObject *type = NULL, *base, *tmptype, *winner;
PyHeapTypeObject *et;
PyMemberDef *mp;
Py_ssize_t i, nbases, nslots, slotoffset, name_size;
int j, may_add_dict, may_add_weak, add_dict, add_weak;
_Py_IDENTIFIER(__qualname__);
_Py_IDENTIFIER(__slots__);
_Py_IDENTIFIER(__classcell__);
/* Special case: type(x) should return x->ob_type */
// 省略
/* Check arguments: (name, bases, dict) */
if (!PyArg_ParseTuple(args, "UO!O!:type.__new__", &na...
&bases, &PyDict_Type, &orig_dic...
return NULL;
// 省略
dict = PyDict_Copy(orig_dict);
/* Check for a __slots__ sequence variable in dict, a...
slots = _PyDict_GetItemId(dict, &PyId___slots__);
nslots = 0;
add_dict = 0;
add_weak = 0;
may_add_dict = base->tp_dictoffset == 0;
may_add_weak = base->tp_weaklistoffset == 0 && base->...
if (slots == NULL) {
// 今回はこっち
// may_add_dictもmay_add_weakも真
if (may_add_dict) {
add_dict++;
}
if (may_add_weak) {
add_weak++;
}
}
else {
// 省略
}
/* Allocate the type object */
type = (PyTypeObject *)metatype->tp_alloc(metatype, n...
// 省略
/* Initialize tp_dict from passed-in dict */
Py_INCREF(dict);
type->tp_dict = dict;
// 省略
/* Add descriptors for custom slots from __slots__, o...
mp = PyHeapType_GET_MEMBERS(et);
slotoffset = base->tp_basicsize;
if (et->ht_slots != NULL) {
// 省略
}
if (add_dict) {
if (base->tp_itemsize)
type->tp_dictoffset = -(long)sizeof(PyObject ...
else
type->tp_dictoffset = slotoffset;
slotoffset += sizeof(PyObject *);
}
if (add_weak) {
assert(!base->tp_itemsize);
type->tp_weaklistoffset = slotoffset;
slotoffset += sizeof(PyObject *);
}
type->tp_basicsize = slotoffset;
type->tp_itemsize = base->tp_itemsize;
type->tp_members = PyHeapType_GET_MEMBERS(et);
// 省略
/* Initialize the rest */
if (PyType_Ready(type) < 0)
goto error;
// 省略
Py_DECREF(dict);
return (PyObject *)type;
}
}}
クラス定義コードを実行して作成した属性辞書が設定されまし...
後、インスタンスが属性アクセスする際に関係ありそうなdicto...
type_callに戻った後tp_initが呼ばれていますが今回の場合は...
*おわりに [#yb88015f]
今回はbuiltin___build_class__の残りの部分について見てきま...
ともかくクラス定義コードを実行して得られた属性辞書(メソ...
終了行:
[[Pythonを読む]]
#contents
*はじめに [#l29d1320]
引き続きbuiltin___build_class__を読んでいきます。builtin_...
+メタクラスの決定
+クラス(メソッドなど)を定義するコードを実行し属性辞書を...
+属性辞書を使ってクラスを作成
「クラス定義コード実行(属性辞書の作成)」の部分
#code(C){{
prep = _PyObject_GetAttrId(meta, &PyId___prepare__);
if (prep == NULL) {
// こっちには来ないはず
}
else {
PyObject *pargs[2] = {name, bases};
ns = _PyObject_FastCallDict(prep, pargs, 2, mkw);
Py_DECREF(prep);
}
cell = PyEval_EvalCodeEx(PyFunction_GET_CODE(func), P...
NULL, 0, NULL, 0, NULL, 0, N...
PyFunction_GET_CLOSURE(func));
}}
_PyObject_FastCallDictはCFunctionObjectの場合、_PyCFuncti...
*PyEval_EvalCodeEx (Python/ceval.c) [#a22d3c92]
さて、PyEval_EvalCodeExを見ていきましょう。前にも見ました...
#code(C){{
PyObject *
PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObj...
PyObject **args, int argcount, PyObject **kws,...
PyObject **defs, int defcount, PyObject *kwdef...
{
return _PyEval_EvalCodeWithName(_co, globals, locals,
args, argcount,
kws, kws != NULL ? kw...
kwcount, 2,
defs, defcount,
kwdefs, closure,
NULL, NULL);
}
}}
呼び出しと照らし合わせると、__prepare__により作成された空...
_PyEval_EvalCodeWithNameに移ります。localsの動きを見てみ...
#code(C){{
static PyObject *
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals...
PyObject **args, Py_ssize_t argcount,
PyObject **kwnames, PyObject **kwargs,
Py_ssize_t kwcount, int kwstep,
PyObject **defs, Py_ssize_t defcount,
PyObject *kwdefs, PyObject *closure,
PyObject *name, PyObject *qualname)
{
PyCodeObject* co = (PyCodeObject*)_co;
PyFrameObject *f;
PyObject *retval = NULL;
PyThreadState *tstate;
// 関係ないローカル変数は省略
/* Create the frame */
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 [#k7b9e41a]
裏は取ってないけど意味を考えると最後のelseが実行されるの...
#code(C){{
PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, Py...
PyObject *locals)
{
// 省略
/* 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();
if (locals == NULL) {
Py_DECREF(f);
return NULL;
}
f->f_locals = locals;
}
else {
if (locals == NULL)
locals = globals;
Py_INCREF(locals);
f->f_locals = locals;
}
// 省略
}
}}
ところで、FrameObjectには実はローカル変数と称しているもの...
分けているのは名にし負わばにfast、高速化のためでしょうが...
**STORE_NAME [#t18d1b10]
ここで件のクラス定義コードを見てみましょう。
>>> dis.dis(co.co_consts[0])
2 0 LOAD_NAME 0 (__name__)
2 STORE_NAME 1 (__module__)
4 LOAD_CONST 0 ('Foo')
6 STORE_NAME 2 (__qualname__)
3 8 LOAD_CONST 1 (<code object...
10 LOAD_CONST 2 ('Foo.__init_...
12 MAKE_FUNCTION 0
14 STORE_NAME 3 (__init__)
6 16 LOAD_CONST 3 (<code object...
18 LOAD_CONST 4 ('Foo.method')
20 MAKE_FUNCTION 0
22 STORE_NAME 4 (method)
24 LOAD_CONST 5 (None)
26 RETURN_VALUE
関数のコードオブジェクトと名前の文字列をスタックに積んで...
#code(C){{
TARGET(STORE_NAME) {
PyObject *name = GETITEM(names, oparg);
PyObject *v = POP();
PyObject *ns = f->f_locals;
int err;
if (PyDict_CheckExact(ns))
err = PyDict_SetItem(ns, name, v);
else
err = PyObject_SetItem(ns, name, v);
Py_DECREF(v);
if (err != 0)
goto error;
DISPATCH();
}
}}
というわけで作られた関数オブジェクト(メソッド定義)がloc...
*クラス作成処理 [#u2746556]
builtin___build_class__に戻って残りの処理です。
#code(C){{
if (cell != NULL) {
PyObject *margs[3] = {name, bases, ns};
cls = _PyObject_FastCallDict(meta, margs, 3, mkw);
// 省略
}
error:
Py_XDECREF(cell);
Py_DECREF(ns);
Py_DECREF(meta);
Py_XDECREF(mkw);
Py_DECREF(bases);
return cls;
}
}}
さきほども出てきた_PyObject_FastCallDictですが今回は対象...
#code(C){{
PyObject *
_PyObject_FastCallDict(PyObject *func, PyObject **args, P...
PyObject *kwargs)
{
ternaryfunc call;
PyObject *result = NULL;
if (Py_EnterRecursiveCall(" while calling a Python ob...
return NULL;
}
if (PyFunction_Check(func)) {
result = _PyFunction_FastCallDict(func, args, nar...
}
else if (PyCFunction_Check(func)) {
result = _PyCFunction_FastCallDict(func, args, na...
}
else {
PyObject *tuple;
/* Slow-path: build a temporary tuple */
call = func->ob_type->tp_call;
tuple = _PyStack_AsTuple(args, nargs);
result = (*call)(func, tuple, kwargs);
Py_DECREF(tuple);
result = _Py_CheckFunctionResult(func, result, NU...
}
exit:
Py_LeaveRecursiveCall();
return result;
}
}}
というわけでtp_callとして設定されているtype_callへ。あ、...
**type_call (Objects/typeobject.c) [#gee9a82f]
#code(C){{
type_call(PyTypeObject *type, PyObject *args, PyObject *k...
{
PyObject *obj;
obj = type->tp_new(type, args, kwds);
obj = _Py_CheckFunctionResult((PyObject*)type, obj, N...
if (obj == NULL)
return NULL;
/* Ugly exception: when the call was type(something),
don't call tp_init on the result. */
if (type == &PyType_Type &&
PyTuple_Check(args) && PyTuple_GET_SIZE(args) == ...
(kwds == NULL ||
(PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))
return obj;
/* If the returned object is not an instance of type,
it won't be initialized. */
if (!PyType_IsSubtype(Py_TYPE(obj), type))
return obj;
type = Py_TYPE(obj);
if (type->tp_init != NULL) {
int res = type->tp_init(obj, args, kwds);
if (res < 0) {
assert(PyErr_Occurred());
Py_DECREF(obj);
obj = NULL;
}
else {
assert(!PyErr_Occurred());
}
}
return obj;
}
}}
本筋ではないですが真ん中あたりでUglyと言われている部分に...
この部分はPythonでtype関数を呼び出した時の処理です。type...
**type_new (Objects/typeobject.c) [#id807c38]
tp_newが指しているtype_newへ。type_newは500行近くあるので...
#code(C){{
static PyObject *
type_new(PyTypeObject *metatype, PyObject *args, PyObject...
{
PyObject *name, *bases = NULL, *orig_dict, *dict = NU...
PyObject *qualname, *slots = NULL, *tmp, *newslots, *...
PyTypeObject *type = NULL, *base, *tmptype, *winner;
PyHeapTypeObject *et;
PyMemberDef *mp;
Py_ssize_t i, nbases, nslots, slotoffset, name_size;
int j, may_add_dict, may_add_weak, add_dict, add_weak;
_Py_IDENTIFIER(__qualname__);
_Py_IDENTIFIER(__slots__);
_Py_IDENTIFIER(__classcell__);
/* Special case: type(x) should return x->ob_type */
// 省略
/* Check arguments: (name, bases, dict) */
if (!PyArg_ParseTuple(args, "UO!O!:type.__new__", &na...
&bases, &PyDict_Type, &orig_dic...
return NULL;
// 省略
dict = PyDict_Copy(orig_dict);
/* Check for a __slots__ sequence variable in dict, a...
slots = _PyDict_GetItemId(dict, &PyId___slots__);
nslots = 0;
add_dict = 0;
add_weak = 0;
may_add_dict = base->tp_dictoffset == 0;
may_add_weak = base->tp_weaklistoffset == 0 && base->...
if (slots == NULL) {
// 今回はこっち
// may_add_dictもmay_add_weakも真
if (may_add_dict) {
add_dict++;
}
if (may_add_weak) {
add_weak++;
}
}
else {
// 省略
}
/* Allocate the type object */
type = (PyTypeObject *)metatype->tp_alloc(metatype, n...
// 省略
/* Initialize tp_dict from passed-in dict */
Py_INCREF(dict);
type->tp_dict = dict;
// 省略
/* Add descriptors for custom slots from __slots__, o...
mp = PyHeapType_GET_MEMBERS(et);
slotoffset = base->tp_basicsize;
if (et->ht_slots != NULL) {
// 省略
}
if (add_dict) {
if (base->tp_itemsize)
type->tp_dictoffset = -(long)sizeof(PyObject ...
else
type->tp_dictoffset = slotoffset;
slotoffset += sizeof(PyObject *);
}
if (add_weak) {
assert(!base->tp_itemsize);
type->tp_weaklistoffset = slotoffset;
slotoffset += sizeof(PyObject *);
}
type->tp_basicsize = slotoffset;
type->tp_itemsize = base->tp_itemsize;
type->tp_members = PyHeapType_GET_MEMBERS(et);
// 省略
/* Initialize the rest */
if (PyType_Ready(type) < 0)
goto error;
// 省略
Py_DECREF(dict);
return (PyObject *)type;
}
}}
クラス定義コードを実行して作成した属性辞書が設定されまし...
後、インスタンスが属性アクセスする際に関係ありそうなdicto...
type_callに戻った後tp_initが呼ばれていますが今回の場合は...
*おわりに [#yb88015f]
今回はbuiltin___build_class__の残りの部分について見てきま...
ともかくクラス定義コードを実行して得られた属性辞書(メソ...
ページ名: