[[Pythonを読む]]
 
 #contents
 
 *はじめに [#t38e4dc8]
 
 それではオブジェクト表現について見ていきます。まずはオブジェクトの素となるクラス定義の処理を見ます。disモジュールで確認したところ以下のようになっていました。
 
    2           0 LOAD_BUILD_CLASS
                1 LOAD_CONST               0 (<code object Foo at 0x01FAD8E0, file "<string>", line 2>)
                2 LOAD_CONST               0 (<code object Foo at 0x031936A8, file "<string>", line 2>)
                4 LOAD_CONST               1 ('Foo')
                7 MAKE_FUNCTION            0
               10 LOAD_CONST               1 ('Foo')
               13 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
               16 STORE_NAME               0 (Foo)
                6 MAKE_FUNCTION            0
                8 LOAD_CONST               1 ('Foo')
               10 CALL_FUNCTION            2
               12 STORE_NAME               0 (Foo)
 
 特徴的なのは、クラス定義のコードオブジェクトを関数オブジェクトにしている点です。
 
 13のCALL_FUNCTION時点でのスタックは以下のようになっています。つまり、'Foo'という名前と「Fooクラス定義の関数オブジェクト」を引数に__build_class__関数が呼び出されます。
 
  'Foo'
  Fooクラス定義の関数オブジェクト
  __build_class__の関数オブジェクト
 
 *Python関数のC実装 [#v9f847d3]
 
 というわけで__build_class__関数の実装であるbuiltin___build_class__を見てみましょう。
 
 ・・・の前に、何故これを見ればいいという話になるかを説明しなければいけませんね。遠回りになりますがPythonの関数とCでの実装の関係をまず確認しましょう。
 
 *builtins [#j0b5bf2c]
 
 まずはLOAD_BUILD_CLASSの処理コードについて見てみましょう。バイトコードに対する処理は[[バイトコード実行を読む>Pyton/バイトコード実行を読む]]で確認したようにceval.cに書かれています。
 
 #code(C){{
         TARGET(LOAD_BUILD_CLASS) {
             _Py_IDENTIFIER(__build_class__);
 
             PyObject *bc;
             if (PyDict_CheckExact(f->f_builtins)) {
                 bc = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
                 if (bc == NULL) {
                     PyErr_SetString(PyExc_NameError,
                                     "__build_class__ not found");
                     goto error;
                 }
                 Py_INCREF(bc);
             }
             else {
                 PyObject *build_class_str = _PyUnicode_FromId(&PyId___build_class__);
                 if (build_class_str == NULL)
                     goto error;
                 bc = PyObject_GetItem(f->f_builtins, build_class_str);
                 if (bc == NULL) {
                     if (PyErr_ExceptionMatches(PyExc_KeyError))
                         PyErr_SetString(PyExc_NameError,
                                         "__build_class__ not found");
                     goto error;
                 }
             }
             PUSH(bc);
             DISPATCH();
         }
 }}
 
 分岐しています。結局やっていることは同じようですが普通はどちらが実行されるのでしょうか。そのためには次にf_builtinsがどう設定されているかを確認する必要があります。
 
 fはPyFrameObjectです。
 
 *PyFrame_New (Objects/frameobject.c) [#t2769619]
 
 PyFrameObjectを作っているのはPyFrame_Newです。以前に見たときはbuiltinsの処理をさっくり省略したので今回はその部分に注目します。
 
 #code(C){{
 PyFrameObject *
 PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
             PyObject *locals)
 {
     PyFrameObject *back = tstate->frame;
     PyFrameObject *f;
     PyObject *builtins;
     Py_ssize_t i;
 
     if (back == NULL || back->f_globals != globals) {
         builtins = _PyDict_GetItemId(globals, &PyId___builtins__);
         if (builtins) {
             if (PyModule_Check(builtins)) {
                 builtins = PyModule_GetDict(builtins);
                 assert(builtins != NULL);
             }
         }
         if (builtins == NULL) {
             // 省略
         }
         else
             Py_INCREF(builtins);
 
     }
     else {
         // 省略
     }
     
     // frameの確保。省略
     
     f->f_stacktop = f->f_valuestack;
     f->f_builtins = builtins;
     Py_XINCREF(back);
     f->f_back = back;
     
     // 変数の設定などなど。省略
 }
 }}
 
 globalsから__builtins__モジュールを取り出してPyFrameObjectに設定しています。
 次はglobalsがどこで設定されているかをたどる必要はありますが、とりあえずわかることとしてbuiltinsは辞書オブジェクトのようです。
 
 *PyRun_InteractiveOneObject (Python/pythonrun.c) [#uf2090ce]
 
 globalsはceval.cにあるPy_EvalCodeに渡されるわけですが大元をたどっていくとPython/pythonrun.cのPyRun_InteractiveOneObjectに至ります(ファイル実行の場合でもやってることは同じです)。エラー処理などはカットして貼ると、
 
 #code(C){{
 int
 PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags)
 {
     PyObject *m, *d, *v, *w, *oenc = NULL, *mod_name;
     mod_ty mod;
     PyArena *arena;
     char *ps1 = "", *ps2 = "", *enc = NULL;
     int errcode = 0;
     _Py_IDENTIFIER(encoding);
     _Py_IDENTIFIER(__main__);
 
     mod_name = _PyUnicode_FromId(&PyId___main__); /* borrowed */
 
     // 途中省略
 
     mod = PyParser_ASTFromFileObject(fp, filename, enc,
                                      Py_single_input, ps1, ps2,
                                      flags, &errcode, arena);
     Py_XDECREF(v);
     Py_XDECREF(w);
     Py_XDECREF(oenc);
     m = PyImport_AddModuleObject(mod_name);
     d = PyModule_GetDict(m);
     v = run_mod(mod, filename, d, d, flags, arena);
     PyArena_Free(arena);
     Py_DECREF(v);
     flush_io();
     return 0;
 }
 }}
 
 PyImport_AddModuleObjectはPython/import.cに書かれていますがその先を追いかけてもbuiltinsの記述は見当たりません。ということはここに来る前、どこかで__main__モジュールはすでに初期化されておりbuiltinsも設定されているということがわかります。
 
 *_Py_InitializeEx_Private (Python/pylifecycle.c) [#lf07e358]
 
 __main__が定義されているところを求めてさらにプログラムの実行をさかのぼります。すると、Py_Main(Modules/main.c)中でPy_Initializeを呼び出しています。これも以前はさっくり無視した部分ですが今回はちゃんと見る必要がありそうです。
 
 Py_Initializeの実体は_Py_InitializeEx_Privateで名前の通りいろいろと初期化をしています。長いので今回も要点だけ抜き出すと、
 
 #code(C){{
 void
 _Py_InitializeEx_Private(int install_sigs, int install_importlib)
 {
     PyInterpreterState *interp;
     PyThreadState *tstate;
     PyObject *bimod, *sysmod, *pstderr;
     extern void _Py_ReadyTypes(void);
 
     // 省略
 
     interp = PyInterpreterState_New();
 
     tstate = PyThreadState_New(interp);
     (void) PyThreadState_Swap(tstate);
 
     _Py_ReadyTypes();
 
     // 省略
 
     bimod = _PyBuiltin_Init();
     _PyImport_FixupBuiltin(bimod, "builtins");
     interp->builtins = PyModule_GetDict(bimod);
     Py_INCREF(interp->builtins);
 
     // 省略
 
     initmain(interp); /* Module __main__ */
 
     // 省略
 }
 }}
 
 builtinsとか__main__が関係ありそうなのはこれぐらいになります。
 
 **_PyBuiltin_Init (Python/bltmodule.c) [#r000b487]
 
 builtinsのオブジェクトを作ってるらしい_PyBuiltin_Initを見てみます。注目点だけ抜き出し以下同様(毎回書くのがめんどくさくなってきた)
 
 #code(C){{
 PyObject *
 _PyBuiltin_Init(void)
 {
     PyObject *mod, *dict, *debug;
 
     mod = PyModule_Create(&builtinsmodule);
     dict = PyModule_GetDict(mod);
 
 #define SETBUILTIN(NAME, OBJECT) \
     if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0)       \
         return NULL;
 
     SETBUILTIN("None",                  Py_None);
     SETBUILTIN("Ellipsis",              Py_Ellipsis);
     // 中略
     SETBUILTIN("type",                  &PyType_Type);
     SETBUILTIN("zip",                   &PyZip_Type);
     debug = PyBool_FromLong(Py_OptimizeFlag == 0);
     if (PyDict_SetItemString(dict, "__debug__", debug) < 0) {
         Py_DECREF(debug);
         return NULL;
     }
     Py_DECREF(debug);
 
     return mod;
 #undef SETBUILTIN
 }
 }}
 
 builtinsmoduleは_PyBuiltin_Initのすぐ上にあります。なお、PyModuleDefの定義はInclude/moduleobject.hに書かれています。
 
 #code(C){{
 static struct PyModuleDef builtinsmodule = {
     PyModuleDef_HEAD_INIT,
     "builtins",
     builtin_doc,
     -1, /* multiple "initialization" just copies the module dict. */
     builtin_methods,
     NULL,
     NULL,
     NULL,
     NULL
 };
 }}
 
 builtin_methodsはさらにその上。PyMethodDefの定義はInclude/methodobject.hです。
 
 #code(C){{
 static PyMethodDef builtin_methods[] = {
     {"__build_class__", (PyCFunction)builtin___build_class__,
      METH_VARARGS | METH_KEYWORDS, build_class_doc},
     {"__import__",      (PyCFunction)builtin___import__, METH_VARARGS | METH_KEYWORDS, import_doc},
     // 中略
     {"vars",            builtin_vars,       METH_VARARGS, vars_doc},
     {NULL,              NULL},
 };
 }}
 
 おっ、__build_class__がありました、ってとこですがまずはbuiltinsの構築を見切ってしまいましょう。
 
 ***PyModule_Create2 (Objects/moduleobject.c) [#h2ba25b8]
 
 モジュールを作成するためのPyModule_Createは、Include/modsupport.hでdefineされているマクロでその実体はPyModule_Create2です。
 
 #code(C){{
 PyObject *
 PyModule_Create2(struct PyModuleDef* module, int module_api_version)
 {
     const char* name;
     PyModuleObject *m;
     PyInterpreterState *interp = PyThreadState_Get()->interp;
     if (!PyModuleDef_Init(module))
         return NULL;
     name = module->m_name;
     if ((m = (PyModuleObject*)PyModule_New(name)) == NULL)
         return NULL;
 
     if (module->m_methods != NULL) {
         if (PyModule_AddFunctions((PyObject *) m, module->m_methods) != 0) {
             Py_DECREF(m);
             return NULL;
         }
     }
     if (module->m_doc != NULL) {
         if (PyModule_SetDocString((PyObject *) m, module->m_doc) != 0) {
             Py_DECREF(m);
             return NULL;
         }
     }
     m->md_def = module;
     return (PyObject*)m;
 }
 }}
 
 PyModule_New、およびそこから呼ばれているPyModule_NewObject
 
 #code(C){{
 PyObject *
 PyModule_New(const char *name)
 {
     PyObject *nameobj, *module;
     nameobj = PyUnicode_FromString(name);
     if (nameobj == NULL)
         return NULL;
     module = PyModule_NewObject(nameobj);
     Py_DECREF(nameobj);
     return module;
 }
 
 PyObject *
 PyModule_NewObject(PyObject *name)
 {
     PyModuleObject *m;
     m = PyObject_GC_New(PyModuleObject, &PyModule_Type);
     m->md_def = NULL;
     m->md_state = NULL;
     m->md_weaklist = NULL;
     m->md_name = NULL;
     m->md_dict = PyDict_New();
     if (module_init_dict(m, m->md_dict, name, NULL) != 0)
         goto fail;
     PyObject_GC_Track(m);
     return (PyObject *)m;
 
  fail:
     Py_DECREF(m);
     return NULL;
 }
 }}
 
 dictが出てきました。なおたびたび出てきているPyModule_GetDictは基本的にこのdictを返しているだけです。
 
 後は、dictに関数が登録されているのを確認できればOKです。というわけでPyModule_AddFunctions
 
 #code(C){{
 int
 PyModule_AddFunctions(PyObject *m, PyMethodDef *functions)
 {
     int res;
     PyObject *name = PyModule_GetNameObject(m);
     if (name == NULL) {
         return -1;
     }
 
     res = _add_methods_to_object(m, name, functions);
     Py_DECREF(name);
     return res;
 }
 
 static int
 _add_methods_to_object(PyObject *module, PyObject *name, PyMethodDef *functions)
 {
     PyObject *func;
     PyMethodDef *fdef;
 
     for (fdef = functions; fdef->ml_name != NULL; fdef++) {
         func = PyCFunction_NewEx(fdef, (PyObject*)module, name);
         if (PyObject_SetAttrString(module, fdef->ml_name, func) != 0) {
             Py_DECREF(func);
             return -1;
         }
         Py_DECREF(func);
     }
 
     return 0;
 }
 }}
 
 C言語の関数に対応するPyCFunctionObjectが作られ、それがモジュールに設定されています。PyObject_SetAttrStringの先は長くなってきたので流れだけ示すと以下のように進み結局md_dictにPyCFunctionObjectが設定されることになります。
 
 +PyObject_SetAttrString (Objects/object.c)
 +PyObject_SetAttr (Objects/object.c)
 +PyObject_GenericSetAttr (Objects/object.c)
 +_PyObject_GenericSetAttrWithDict (Objects/object.c)
 ++_PyObject_GetDictPtr (Objects/object.c)
 +_PyObjectDict_SetItem (Objects/dictobject.c)
 +PyDict_SetItem (Objects/dictobject.c)
 
 **initmain (Python/pylifecycle.c) [#e66b2674]
 
 pylifecycle.cに戻って、最後に__main__モジュールが設定されるのを確認します。と言ってもまあやってることは単純です。
 
 #code(C){{
 initmain(PyInterpreterState *interp)
 {
     PyObject *m, *d, *loader, *ann_dict;
     m = PyImport_AddModule("__main__");
     d = PyModule_GetDict(m);
     ann_dict = PyDict_New();
     Py_DECREF(ann_dict);
     if (PyDict_GetItemString(d, "__builtins__") == NULL) {
         PyObject *bimod = PyImport_ImportModule("builtins");
         if (PyDict_SetItemString(d, "__builtins__", bimod) < 0) {
             Py_FatalError("Failed to initialize __main__.__builtins__");
         }
         Py_DECREF(bimod);
     }
     
     // 省略
 }
 }}
 
 これで__main__.__builtins__が設定されていることも確認できました。
 
 *おわりに [#t936ed71]
 
 今回はクラスの定義処理を見るに先立ってそもそもPythonの関数呼び出したらC言語で実装された関数が呼び出される仕組み、
 の前にbuiltinsがどうなっているのかを見てきました。タイトルに反して逆算式に見てきましたがこれぐらい規模がでかいソフトだと先頭から見ていくよりも使われているところから先頭に戻ってく方がわかりやすいかなーと思います。
 
 ここまでに出てきて、しれっと流している要素があります。そう、PyObjectです。次回はこいつを中心に関数呼び出しの際に起こっていることを見ていく予定です。

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS