[[Pythonを読む]]
 
 #contents
 
 *はじめに [#wbc2cbcd]
 
 __build_class__が実行される様を見ていきます。以前にも見ましたが起点となるCALL_FUNCTION
 
 #code(C){{
         TARGET(CALL_FUNCTION) {
             PyObject **sp, *res;
             PCALL(PCALL_ALL);
             sp = stack_pointer;
             res = call_function(&sp, oparg, NULL);
             stack_pointer = sp;
             PUSH(res);
             if (res == NULL) {
                 goto error;
             }
             DISPATCH();
         }
 }}
 
 前はcall_functionの先は概要だけ説明しましたが今回はこの先についてガチに見ていきます。
 
 *call_function (Python/ceval.c) [#q76328c9]
 
 call_functionはバイトコード実行と同じceval.cに書かれています。
 
 #code(C){{
 static PyObject *
 call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames)
 {
     PyObject **pfunc = (*pp_stack) - oparg - 1;
     PyObject *func = *pfunc;
     PyObject *x, *w;
     Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
     Py_ssize_t nargs = oparg - nkwargs;
     PyObject **stack;
 
     /* Always dispatch PyCFunction first, because these are
        presumed to be the most frequent callable object.
     */
     if (PyCFunction_Check(func)) {
         PyThreadState *tstate = PyThreadState_GET();
 
         PCALL(PCALL_CFUNCTION);
 
         stack = (*pp_stack) - nargs - nkwargs;
         C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames));
     }
     else {
         // Pythonで書かれた関数。省略
     }
 
     assert((x != NULL) ^ (PyErr_Occurred() != NULL));
 
     /* Clear the stack of the function object.  Also removes
        the arguments in case they weren't consumed already
        (fast_function() and err_args() leave them on the stack).
      */
     while ((*pp_stack) > pfunc) {
         w = EXT_POP(*pp_stack);
         Py_DECREF(w);
         PCALL(PCALL_POP);
     }
 
     return x;
 }
 }}
 
 __build_class__の実装はCで書かれているので今回はifの部分が実行されます。
 Cで実装された関数を実行する_PyCFunction_FastCallKeywordsについて見る前に、そろそろPyObjectについて見ていくことにしましょう。
 
 *PyObject (Include/object.h) [#hd51e3d0]
 
 PyObjectはその名の通りPythonオブジェクトのC表現です。
 
 #code(C){{
 typedef struct _object {
     _PyObject_HEAD_EXTRA
     Py_ssize_t ob_refcnt;
     struct _typeobject *ob_type;
 } PyObject;
 }}
 
 _PyObject_HEAD_EXTRAはデバッグオプション付きでビルドされたときのデバッグ情報用メンバなので今回は無視します。参照カウントとTypeObjectへの参照を持っているようです。
 
 TypeObjectを見る前に、__build_class__はPyCFunctionObjectなわけですがこの定義がどうなっているかというと(Include/methodobject.hです)、
 
 #code(C){{
 typedef struct {
     PyObject_HEAD
     PyMethodDef *m_ml; /* Description of the C function to call */
     PyObject    *m_self; /* Passed as 'self' arg to the C func, can be NULL */
     PyObject    *m_module; /* The __module__ attribute, can be anything */
     PyObject    *m_weakreflist; /* List of weak references */
 } PyCFunctionObject;
 }}
 
 PyObject_HEADの定義はInclude/object.hに戻って、
 
 #code(C){{
 /* PyObject_HEAD defines the initial segment of every PyObject. */
 #define PyObject_HEAD                   PyObject ob_base;
 }}
 
 つまり、PyCFunctionObjectの先頭部分はPyObjectということになります。Cに不慣れな方は「え?これ構造体の中に構造体埋め込まれてるけどfunc->ob_base.ob_refcntみたいにアクセスすんの?」と思われるかもしれませんが、そこはポインタマジック、ポインタ経由でアクセスするとぶっちゃけ以下のように定義されているのと同じようにアクセスできます。
 
 #code(C){{
 typedef struct {
     Py_ssize_t ob_refcnt;
     struct _typeobject *ob_type;
     PyMethodDef *m_ml;
     PyObject    *m_self;
     PyObject    *m_module;
     PyObject    *m_weakreflist;
 } PyCFunctionObject;
 }}
 
 全てのオブジェクト表現が共通のヘッダを持っているため、操作をする際には共通要素に依存した処理が書けることになります。
 ちなみに、PyCFunction_Checkの実装は以下の通りです。
 
 #code(C){{
 #define PyCFunction_Check(op) (Py_TYPE(op) == &PyCFunction_Type)
 }}
 
 Py_TYPEはob_typeメンバを取り出すマクロです。
 
 *PyTypeObject (Include/object.h) [#b227591f]
 
 さて次にPyTypeObjectです。
 
 #code(C){{
 typedef struct _typeobject {
     PyObject_VAR_HEAD
     const char *tp_name; /* For printing, in format "<module>.<name>" */
     Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
 
     /* Methods to implement standard operations */
 
     destructor tp_dealloc;
     printfunc tp_print;
     getattrfunc tp_getattr;
     setattrfunc tp_setattr;
     PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)
                                     or tp_reserved (Python 3) */
     reprfunc tp_repr;
 
     /* Method suites for standard classes */
 
     PyNumberMethods *tp_as_number;
     PySequenceMethods *tp_as_sequence;
     PyMappingMethods *tp_as_mapping;
 
     /* More standard operations (here for binary compatibility) */
 
     hashfunc tp_hash;
     ternaryfunc tp_call;
     reprfunc tp_str;
     getattrofunc tp_getattro;
     setattrofunc tp_setattro;
 
     /* Functions to access object as input/output buffer */
     PyBufferProcs *tp_as_buffer;
 
     /* Flags to define presence of optional/expanded features */
     unsigned long tp_flags;
 
     const char *tp_doc; /* Documentation string */
 
     /* Assigned meaning in release 2.0 */
     /* call function for all accessible objects */
     traverseproc tp_traverse;
 
     /* delete references to contained objects */
     inquiry tp_clear;
 
     /* Assigned meaning in release 2.1 */
     /* rich comparisons */
     richcmpfunc tp_richcompare;
 
     /* weak reference enabler */
     Py_ssize_t tp_weaklistoffset;
 
     /* Iterators */
     getiterfunc tp_iter;
     iternextfunc tp_iternext;
 
     /* Attribute descriptor and subclassing stuff */
     struct PyMethodDef *tp_methods;
     struct PyMemberDef *tp_members;
     struct PyGetSetDef *tp_getset;
     struct _typeobject *tp_base;
     PyObject *tp_dict;
     descrgetfunc tp_descr_get;
     descrsetfunc tp_descr_set;
     Py_ssize_t tp_dictoffset;
     initproc tp_init;
     allocfunc tp_alloc;
     newfunc tp_new;
     freefunc tp_free; /* Low-level free-memory routine */
     inquiry tp_is_gc; /* For PyObject_IS_GC */
     PyObject *tp_bases;
     PyObject *tp_mro; /* method resolution order */
     PyObject *tp_cache;
     PyObject *tp_subclasses;
     PyObject *tp_weaklist;
     destructor tp_del;
 
     /* Type attribute cache version tag. Added in version 2.6 */
     unsigned int tp_version_tag;
 
     destructor tp_finalize;
 } PyTypeObject;
 }}
 
 長い、ってところですがメンバについて詳しいことは次回以降に見ていきます。ともかく次にPyCFunction_Typeの定義を見てみましょう。Objects/methodobject.cです。
 
 #code(C){{
 PyTypeObject PyCFunction_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     "builtin_function_or_method",
     sizeof(PyCFunctionObject),
     0,
     (destructor)meth_dealloc,                   /* tp_dealloc */
     0,                                          /* tp_print */
     0,                                          /* tp_getattr */
     0,                                          /* tp_setattr */
     0,                                          /* tp_reserved */
     (reprfunc)meth_repr,                        /* tp_repr */
     0,                                          /* tp_as_number */
     0,                                          /* tp_as_sequence */
     0,                                          /* tp_as_mapping */
     (hashfunc)meth_hash,                        /* tp_hash */
     PyCFunction_Call,                           /* tp_call */
     0,                                          /* tp_str */
     PyObject_GenericGetAttr,                    /* tp_getattro */
     0,                                          /* tp_setattro */
     0,                                          /* tp_as_buffer */
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
     0,                                          /* tp_doc */
     (traverseproc)meth_traverse,                /* tp_traverse */
     0,                                          /* tp_clear */
     meth_richcompare,                           /* tp_richcompare */
     offsetof(PyCFunctionObject, m_weakreflist), /* tp_weaklistoffset */
     0,                                          /* tp_iter */
     0,                                          /* tp_iternext */
     meth_methods,                               /* tp_methods */
     meth_members,                               /* tp_members */
     meth_getsets,                               /* tp_getset */
     0,                                          /* tp_base */
     0,                                          /* tp_dict */
 };
 }}
 
 今回の本筋ではありませんが説明しないわけにもいかないので、PyCFunction_Typeはob_typeとしてPyType_Typeを参照します。つまり、「PyCFunctionはType型」ということになります。Type型は「型の型」、メタな型です。
 
 で、ともかくPyCFunction型が定義されました。
 
 **PyObjectとPyTypeObjectの関連付け [#kdc4b44d]
 
 大体わかってきましたがPyTypeObjectはPyObjectの型を表すオブジェクトです。別の言語風に言うとクラスオブジェクトといったところでしょうか。一応、オブジェクトが生成されるときにPyTypeObjectが設定される様子を確認しておきましょう。前回は中に踏み込まなかったPyCFunction_NewEx
 
 #code(C){{
 PyObject *
 PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module)
 {
     PyCFunctionObject *op;
     op = free_list;
     if (op != NULL) {
         free_list = (PyCFunctionObject *)(op->m_self);
         (void)PyObject_INIT(op, &PyCFunction_Type);
         numfree--;
     }
     else {
         op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type);
         if (op == NULL)
             return NULL;
     }
     op->m_weakreflist = NULL;
     op->m_ml = ml;
     Py_XINCREF(self);
     op->m_self = self;
     Py_XINCREF(module);
     op->m_module = module;
     _PyObject_GC_TRACK(op);
     return (PyObject *)op;
 }
 }}
 
 PyObject_GC_New、Include/objimpl.hを経由して実体はModule/gcmodule.cの_PyObject_GC_New
 
 #code(C){{
 _PyObject_GC_New(PyTypeObject *tp)
 {
     PyObject *op = _PyObject_GC_Malloc(_PyObject_SIZE(tp));
     if (op != NULL)
         op = PyObject_INIT(op, tp);
     return op;
 }
 }}
 
 PyObject_INITはまたInclude/objimpl.hに戻ります。
 
 #code(C){{
 #define PyObject_INIT(op, typeobj) \
     ( Py_TYPE(op) = (typeobj), _Py_NewReference((PyObject *)(op)), (op) )
 }}
 
 というわけでPyObjectとPyTypeObjectが関連付けられました。
 
 *_PyCFunction_FastCallKeywords (Objects/methodobject.c) [#r1b91b9f]
 
 長くなりましたがPyObjectについて確認できたのでオブジェクトの型がPyCFunction_Typeの時に実行される_PyCFunction_FastCallKeywordsに進みます。
 
 #code(C){{
 PyObject *
 _PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack,
                               Py_ssize_t nargs, PyObject *kwnames)
 {
     PyObject *kwdict, *result;
     Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
 
     /* kwnames must only contains str strings, no subclass, and all keys must
        be unique */
 
     if (nkwargs > 0) {
         kwdict = _PyStack_AsDict(stack + nargs, kwnames);
         if (kwdict == NULL) {
             return NULL;
         }
     }
     else {
         kwdict = NULL;
     }
 
     result = _PyCFunction_FastCallDict(func, stack, nargs, kwdict);
     Py_XDECREF(kwdict);
     return result;
 }
 }}
 
 今回はnkwargsは0なのでそのまま_PyCFunction_FastCallDictへ。
 長いので実行される部分以外は省略。
 
 #code(C){{
 PyObject *
 _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs,
                           PyObject *kwargs)
 {
     PyCFunctionObject *func = (PyCFunctionObject*)func_obj;
     PyCFunction meth = PyCFunction_GET_FUNCTION(func);
     PyObject *self = PyCFunction_GET_SELF(func);
     PyObject *result;
     int flags;
 
     /* _PyCFunction_FastCallDict() must not be called with an exception set,
        because it may clear it (directly or indirectly) and so the
        caller loses its exception */
 
     flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST);
 
     switch (flags)
     {
     case METH_NOARGS:
         // 省略
 
     case METH_O:
         // 省略
 
     case METH_VARARGS:
     case METH_VARARGS | METH_KEYWORDS:
     {
         /* Slow-path: create a temporary tuple */
         PyObject *tuple;
 
         if (!(flags & METH_KEYWORDS) && kwargs != NULL && PyDict_Size(kwargs) != 0) {
             PyErr_Format(PyExc_TypeError,
                          "%.200s() takes no keyword arguments",
                          func->m_ml->ml_name);
             return NULL;
         }
 
         tuple = _PyStack_AsTuple(args, nargs);
         if (tuple == NULL) {
             return NULL;
         }
 
         if (flags & METH_KEYWORDS) {
             result = (*(PyCFunctionWithKeywords)meth) (self, tuple, kwargs);
         }
         else {
             result = (*meth) (self, tuple);
         }
         Py_DECREF(tuple);
         break;
     }
 
     case METH_FASTCALL:
         // 省略
 
     default:
         PyErr_SetString(PyExc_SystemError,
                         "Bad call flags in PyCFunction_Call. "
                         "METH_OLDARGS is no longer supported!");
         return NULL;
     }
 
     result = _Py_CheckFunctionResult(func_obj, result, NULL);
 
     return result;
 }
 }}
 
 というわけで、Python関数に対応するC言語の関数が実行されることになります。この先は次回。
 
 *おわりに [#oc6198da]
 
 今回は関数実行をスタートにPyObjectについて見ていきました。
 本当はPyTypeObjectについてもう少し触れようと思ったのですがC実装の関数の場合はTypeObectは識別子ぐらいにしか使われていないので省略しました。心配しなくても(?)後からがっつり見ていくことになります。
 
 ともかくこれでようやく__build_class__に対するbuiltin___build_class__が実行される段階まで来ました。
 

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