Pythonを読む
はじめに †
引き続きbuiltin___build_class__を読んでいきます。builtin___build_class__では以下の処理を行っていました。
- メタクラスの決定
- クラス(メソッドなど)を定義するコードを実行し属性辞書を作成
- 属性辞書を使ってクラスを作成
「クラス定義コード実行(属性辞書の作成)」の部分
1
2
3
4
5
6
7
8
9
10
11
12
|
-
-
!
-
!
|
|
|
| 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), PyFunction_GET_GLOBALS(func), ns,
NULL, 0, NULL, 0, NULL, 0, NULL,
PyFunction_GET_CLOSURE(func));
|
_PyObject_FastCallDictはCFunctionObjectの場合、_PyCFunction_FastCallKeywordsに処理が移譲されています。つまり、Cの関数が呼ばれて結果が返されます。結局、nsは空の辞書オブジェクトを参照することになります。
PyEval_EvalCodeEx (Python/ceval.c) †
さて、PyEval_EvalCodeExを見ていきましょう。前にも見ましたがその時はあまり変数について気にしませんでした。今回は変数が重要になってくるのでその部分をちゃんと確認しましょう。そもそもクラス定義コード実行ってどういうことでしょうか。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
-
|
|
|
|
|
|
|
!
| PyObject *
PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
PyObject **args, int argcount, PyObject **kws, int kwcount,
PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure)
{
return _PyEval_EvalCodeWithName(_co, globals, locals,
args, argcount,
kws, kws != NULL ? kws + 1 : NULL,
kwcount, 2,
defs, defcount,
kwdefs, closure,
NULL, NULL);
}
|
呼び出しと照らし合わせると、__prepare__により作成された空の辞書オブジェクトはlocalsにあたることがわかります。globalsは今回は関係ないので無視。ちなみに、クラス定義でクロージャが出てくるのは関数が渡された引数使ってクラス定義するときぐらいですね。そんな処理が必要になるのはDjangoとかのフレームワークぐらいだと思いますが(笑)
_PyEval_EvalCodeWithNameに移ります。localsの動きを見てみるとPyFrame_Newに渡され、その後出てきません。つまり、PyFrame_Newでもう設定は完了しているということになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
-
|
|
|
|
-
!
|
|
|
|
-
!
|
|
|
|
-
|
|
|
!
|
|
|
|
!
| static PyObject *
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
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;
tstate = PyThreadState_GET();
f = PyFrame_New(tstate, co, globals, locals);
retval = PyEval_EvalFrameEx(f,0);
fail:
++tstate->recursion_depth;
Py_DECREF(f);
--tstate->recursion_depth;
return retval;
}
|
PyFrame_New †
裏は取ってないけど意味を考えると最後のelseが実行されるのでしょうね。ともかくこれで空辞書がローカル変数としてセットされました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
-
-
!
|
|
|
-
|
-
|
|
!
|
!
-
|
|
|
|
!
|
-
!
| PyFrameObject *
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
PyObject *locals)
{
if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) == (CO_NEWLOCALS | CO_OPTIMIZED))
;
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には実はローカル変数と称しているものが2つあります。今見たf_localsとf_localsplusです。前者は辞書、後者はよくあるローカル変数、つまり、スタック領域を使ったローカル変数のようです(もっともPythonの場合フレームは連続した領域に確保されるとは限らないようですが)。
分けているのは名にし負わばにfast、高速化のためでしょうが今回は本筋ではないので詳しくは追いかけません。
STORE_NAME †
ここで件のクラス定義コードを見てみましょう。
>>> 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 __init__ at 0x03199A18, file "<string>", line 3>)
10 LOAD_CONST 2 ('Foo.__init__')
12 MAKE_FUNCTION 0
14 STORE_NAME 3 (__init__)
6 16 LOAD_CONST 3 (<code object method at 0x031992E0, file "<string>", line 6>)
18 LOAD_CONST 4 ('Foo.method')
20 MAKE_FUNCTION 0
22 STORE_NAME 4 (method)
24 LOAD_CONST 5 (None)
26 RETURN_VALUE
関数のコードオブジェクトと名前の文字列をスタックに積んで(LOAD_CONST)、MAKE_FUNCTIONして(ここで先に積んだ2オブジェクトが使われて1つの関数オブジェクトがスタックに積まれます)、STORE_NAMEと。見るべきなのはSTORE__NAME処理部分です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| -
|
|
|
|
|
|
|
|
|
|
|
|
!
| 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();
}
|
というわけで作られた関数オブジェクト(メソッド定義)がlocalsに設定されました。
クラス作成処理 †
builtin___build_class__に戻って残りの処理です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| -
|
|
|
-
!
|
|
|
|
|
|
|
!
| 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ですが今回は対象のオブジェクトがType_Typeなので最後のelse部分が実行されます。_PyObject_FastCallDictはObjects/abstract.cに書かれています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
-
|
|
|
-
|
!
|
-
|
!
-
|
!
-
|
|
|
|
|
|
|
|
|
|
|
!
|
|
|
|
|
!
| PyObject *
_PyObject_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs,
PyObject *kwargs)
{
ternaryfunc call;
PyObject *result = NULL;
if (Py_EnterRecursiveCall(" while calling a Python object")) {
return NULL;
}
if (PyFunction_Check(func)) {
result = _PyFunction_FastCallDict(func, args, nargs, kwargs);
}
else if (PyCFunction_Check(func)) {
result = _PyCFunction_FastCallDict(func, args, nargs, kwargs);
}
else {
PyObject *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, NULL);
}
exit:
Py_LeaveRecursiveCall();
return result;
}
|
というわけでtp_callとして設定されているtype_callへ。あ、ちなみにcallというのは、foo()的に()をつけて呼び出した場合という意味です。これ後でも出てくるので覚えておいてください。
type_call (Objects/typeobject.c) †
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
-
|
|
|
|
|
|
|
-
!
|
|
|
|
|
|
-
!
|
|
|
|
-
|
-
|
|
|
!
-
|
!
!
|
!
| type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *obj;
obj = type->tp_new(type, args, kwds);
obj = _Py_CheckFunctionResult((PyObject*)type, obj, NULL);
if (obj == NULL)
return NULL;
if (type == &PyType_Type &&
PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 &&
(kwds == NULL ||
(PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))
return obj;
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('foo')」のように呼び出すと'foo'のクラスオブジェクトを返します。ここではその処理を行っています。
type_new (Objects/typeobject.c) †
tp_newが指しているtype_newへ。type_newは500行近くあるので要点だけ。と言っても長いですが。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
-
|
|
|
|
|
|
|
|
|
|
|
|
-
!
|
|
|
|
|
-
!
|
|
|
|
|
|
|
|
|
-
-
|
-
|
!
-
|
!
!
-
-
!
-
!
-
!
-
-
!
-
!
-
!
|
|
|
|
-
!
|
|
|
|
-
!
|
|
!
| static PyObject *
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{
PyObject *name, *bases = NULL, *orig_dict, *dict = NULL;
PyObject *qualname, *slots = NULL, *tmp, *newslots, *cell;
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__);
if (!PyArg_ParseTuple(args, "UO!O!:type.__new__", &name, &PyTuple_Type,
&bases, &PyDict_Type, &orig_dict))
return NULL;
dict = PyDict_Copy(orig_dict);
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->tp_itemsize == 0;
if (slots == NULL) {
if (may_add_dict) {
add_dict++;
}
if (may_add_weak) {
add_weak++;
}
}
else {
}
type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots);
Py_INCREF(dict);
type->tp_dict = dict;
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);
if (PyType_Ready(type) < 0)
goto error;
Py_DECREF(dict);
return (PyObject *)type;
}
|
クラス定義コードを実行して作成した属性辞書が設定されました。
後、インスタンスが属性アクセスする際に関係ありそうなdictoffsetの設定とPyType_Readyが呼ばれているんだなということを確認しておきます。
type_callに戻った後tp_initが呼ばれていますが今回の場合は目立った処理はしてないので省略。また後で出てきます。
おわりに †
今回はbuiltin___build_class__の残りの部分について見てきました。Pythonのクラス定義にはぶっちゃけなんでも書けます。そのからくりは「そもそも普通に関数実行してるだけだし」ということでした。「デコレータってつまり関数呼び出しなわけだけどなんでこういう風に書けるの?」と思ってましたが本当に関数実行していたわけですね(笑)
ともかくクラス定義コードを実行して得られた属性辞書(メソッド名: 関数オブジェクト)をType_Typeのtp_callを呼び出してクラスオブジェクトを作成します。さっくり省略していますがまあそこは実際に使われている箇所と照らし合わせてみないとわかりにくいので。というわけで続いてインスタンス生成について見ていきましょう。
|