Python/build_class前編(というよりPyTypeObject)
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[Pythonを読む]]
#contents
*はじめに [#v4d88ea9]
ようやく__build_class__関数の実装であるbuiltin___build_cl...
builtin___build_class__は長いので段階的に見ていきます。大...
+メタクラスの決定
+クラス(メソッドなど)を定義するコードを実行し属性辞書を...
+属性辞書を使ってクラスを作成
*メタクラスの決定 [#g240d9e5]
長くなるので引数チェック(エラー終了)系はカットします。
#code(C){{
static PyObject *
builtin___build_class__(PyObject *self, PyObject *args, P...
{
PyObject *func, *name, *bases, *mkw, *meta, *winner, ...
PyObject *cls = NULL, *cell = NULL;
Py_ssize_t nargs;
int isclass = 0; /* initialize to prevent gcc warni...
nargs = PyTuple_GET_SIZE(args);
func = PyTuple_GET_ITEM(args, 0); /* Better be callab...
name = PyTuple_GET_ITEM(args, 1);
bases = PyTuple_GetSlice(args, 2, nargs);
if (kwds == NULL) {
meta = NULL;
mkw = NULL;
}
else {
// メタクラスが指定された場合の処理
}
if (meta == NULL) {
/* if there are no bases, use type: */
if (PyTuple_GET_SIZE(bases) == 0) {
meta = (PyObject *) (&PyType_Type);
}
/* else get the type of the first base */
else {
PyObject *base0 = PyTuple_GET_ITEM(bases, 0);
meta = (PyObject *) (base0->ob_type);
}
Py_INCREF(meta);
isclass = 1; /* meta is really a class */
}
if (isclass) {
/* meta is really a class, so check for a more de...
metaclass, or possible metaclass conflicts: */
winner = (PyObject *)_PyType_CalculateMetaclass((...
b...
if (winner != meta) {
Py_DECREF(meta);
meta = winner;
Py_INCREF(meta);
}
}
/* else: meta is not a class, so we cannot do the met...
calculation, so we will use the explicitly given o...
}}
まずはここまで。kwdsがNULLじゃないときというのは「class F...
今回はメタクラスは指定しておらず、またスーパークラスも指...
*クラス定義コード実行(属性辞書の作成) [#f5c7c1c7]
#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_GetAttrId (Objects/object.c) [#j865bb14]
まず_PyObject_GetAttrId。これはPyObject_GetAttrを呼んでる...
#code(C){{
PyObject *
_PyObject_GetAttrId(PyObject *v, _Py_Identifier *name)
{
PyObject *result;
PyObject *oname = _PyUnicode_FromId(name); /* borrowe...
if (!oname)
return NULL;
result = PyObject_GetAttr(v, oname);
return result;
}
}}
続いてPyObject_GetAttr
#code(C){{
PyObject *
PyObject_GetAttr(PyObject *v, PyObject *name)
{
PyTypeObject *tp = Py_TYPE(v);
if (!PyUnicode_Check(name)) {
PyErr_Format(PyExc_TypeError,
"attribute name must be string, not ...
name->ob_type->tp_name);
return NULL;
}
if (tp->tp_getattro != NULL)
return (*tp->tp_getattro)(v, name);
if (tp->tp_getattr != NULL) {
char *name_str = PyUnicode_AsUTF8(name);
if (name_str == NULL)
return NULL;
return (*tp->tp_getattr)(v, name_str);
}
PyErr_Format(PyExc_AttributeError,
"'%.50s' object has no attribute '%U'",
tp->tp_name, name);
return NULL;
}
}}
はい、TypeObjectが出てきました。さらにそのメンバの関数ポ...
ここでType_Typeの定義(Objects/typeobject.c)を見てみる。
#code(C){{
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
// 省略
(getattrofunc)type_getattro, /* tp_get...
(setattrofunc)type_setattro, /* tp_set...
}}
というわけで、type_getattroが呼び出されることになります。
#code(C){{
static PyObject *
type_getattro(PyTypeObject *type, PyObject *name)
{
PyTypeObject *metatype = Py_TYPE(type);
PyObject *meta_attribute, *attribute;
descrgetfunc meta_get;
/* Initialize this type (we'll assume the metatype is...
if (type->tp_dict == NULL) {
if (PyType_Ready(type) < 0)
return NULL;
}
/* No readable descriptor found yet */
meta_get = NULL;
/* Look for the attribute in the metatype */
meta_attribute = _PyType_Lookup(metatype, name);
if (meta_attribute != NULL) {
// デスクリプタの処理。省略
Py_INCREF(meta_attribute);
}
/* No data descriptor found on metatype. Look in tp_d...
* type and its bases */
attribute = _PyType_Lookup(type, name);
if (attribute != NULL) {
/* Implement descriptor functionality, if any */
descrgetfunc local_get = Py_TYPE(attribute)->tp_d...
Py_XDECREF(meta_attribute);
// デスクリプタの処理。省略
Py_INCREF(attribute);
return attribute;
}
// 今回のケースではここまでで返ってると思うので省略
}
}}
今の場合、typeはType_Typeが渡されているので、TYPEはType_T...
_PyType_Lookupは基本的にはtp_dictから指定された属性を取得...
** PyType_Ready (Objects/typeobject.c) [#e8aab319]
さて、追っかけないといけない方の話をしましょう。PyType_Re...
なお、実際には_Py_InitializeEx_Privateの中で_Py_ReadyType...
PyType_Readyは長いので、__prepare__がtp_dictに登録される...
#code(C){{
static PyObject *
type_prepare(PyObject *self, PyObject *args, PyObject *kw...
{
return PyDict_New();
}
static PyMethodDef type_methods[] = {
// 省略
{"__prepare__", (PyCFunction)type_prepare,
METH_VARARGS | METH_KEYWORDS | METH_CLASS,
PyDoc_STR("__prepare__() -> dict\n"
"used to create the namespace for the clas...
// 省略
{0}
};
}}
PyMethodDefは前にbultinでも出てきましたね。type_methodsは...
#code(C){{
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
// 省略
type_members, /* tp_mem...
type_members, /* tp_mem...
type_getsets, /* tp_get...
}}
で、PyType_Ready中の登録してそうなところ
#code(C){{
/* Initialize tp_dict */
dict = type->tp_dict;
if (dict == NULL) {
dict = PyDict_New();
if (dict == NULL)
goto error;
type->tp_dict = dict;
}
/* Add type-specific descriptors to tp_dict */
if (add_operators(type) < 0)
goto error;
if (type->tp_methods != NULL) {
if (add_methods(type, type->tp_methods) < 0)
goto error;
}
if (type->tp_members != NULL) {
if (add_members(type, type->tp_members) < 0)
goto error;
}
if (type->tp_getset != NULL) {
if (add_getset(type, type->tp_getset) < 0)
goto error;
}
}}
add_methodsに進む。なお、add_operatorはTypeObjectに直接書...
#code(C){{
static int
add_methods(PyTypeObject *type, PyMethodDef *meth)
{
PyObject *dict = type->tp_dict;
for (; meth->ml_name != NULL; meth++) {
PyObject *descr;
int err;
if (PyDict_GetItemString(dict, meth->ml_name) && ...
continue;
if (meth->ml_flags & METH_CLASS) {
descr = PyDescr_NewClassMethod(type, meth);
}
else if (meth->ml_flags & METH_STATIC) {
// 省略
}
else {
descr = PyDescr_NewMethod(type, meth);
}
if (descr == NULL)
return -1;
err = PyDict_SetItemString(dict, meth->ml_name, d...
Py_DECREF(descr);
if (err < 0)
return -1;
}
return 0;
}
}}
あれ?デスクリプタ?先ほどtype_getattroでデスクリプタの場...
#code(C){{
PyObject *
PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *m...
{
PyMethodDescrObject *descr;
descr = (PyMethodDescrObject *)descr_new(&PyClassMeth...
type, method...
if (descr != NULL)
descr->d_method = method;
return (PyObject *)descr;
}
}}
**type_getattro再び [#xb78dfc0]
では読み違えていたところに戻りましょう。
#code(C){{
static PyObject *
type_getattro(PyTypeObject *type, PyObject *name)
{
// 省略
if (meta_attribute != NULL) {
meta_get = Py_TYPE(meta_attribute)->tp_descr_get;
if (meta_get != NULL && PyDescr_IsData(meta_attri...
/* Data descriptors implement tp_descr_set to...
* writes. Assume the attribute is not overri...
* type's tp_dict (and bases): call the descr...
*/
return meta_get(meta_attribute, (PyObject *)t...
(PyObject *)metatype);
}
Py_INCREF(meta_attribute);
}
/* No data descriptor found on metatype. Look in tp_d...
* type and its bases */
attribute = _PyType_Lookup(type, name);
if (attribute != NULL) {
/* Implement descriptor functionality, if any */
descrgetfunc local_get = Py_TYPE(attribute)->tp_d...
Py_XDECREF(meta_attribute);
if (local_get != NULL) {
/* NULL 2nd argument indicates the descriptor...
* found on the target object itself (or a ba...
return local_get(attribute, (PyObject *)NULL,
(PyObject *)type);
}
Py_INCREF(attribute);
return attribute;
}
}}
前述したように今はtypeもmetatype同じになりますが、metaの...
というわけでdescr_getとして呼び出されるclassmethod_get。...
#code(C){{
static PyObject *
classmethod_get(PyMethodDescrObject *descr, PyObject *obj...
{
return PyCFunction_NewEx(descr->d_method, type, NULL);
}
}}
ここでCFunctionObjectにラップされるか。
回り道が長引いたので一旦ここで区切ります。
*おわりに [#b9ab35bb]
今回は__build_class__、というよりCでのオブジェクト実装の...
また、PyTypeObjectに直接書かれている特殊メソッドおよび通...
終了行:
[[Pythonを読む]]
#contents
*はじめに [#v4d88ea9]
ようやく__build_class__関数の実装であるbuiltin___build_cl...
builtin___build_class__は長いので段階的に見ていきます。大...
+メタクラスの決定
+クラス(メソッドなど)を定義するコードを実行し属性辞書を...
+属性辞書を使ってクラスを作成
*メタクラスの決定 [#g240d9e5]
長くなるので引数チェック(エラー終了)系はカットします。
#code(C){{
static PyObject *
builtin___build_class__(PyObject *self, PyObject *args, P...
{
PyObject *func, *name, *bases, *mkw, *meta, *winner, ...
PyObject *cls = NULL, *cell = NULL;
Py_ssize_t nargs;
int isclass = 0; /* initialize to prevent gcc warni...
nargs = PyTuple_GET_SIZE(args);
func = PyTuple_GET_ITEM(args, 0); /* Better be callab...
name = PyTuple_GET_ITEM(args, 1);
bases = PyTuple_GetSlice(args, 2, nargs);
if (kwds == NULL) {
meta = NULL;
mkw = NULL;
}
else {
// メタクラスが指定された場合の処理
}
if (meta == NULL) {
/* if there are no bases, use type: */
if (PyTuple_GET_SIZE(bases) == 0) {
meta = (PyObject *) (&PyType_Type);
}
/* else get the type of the first base */
else {
PyObject *base0 = PyTuple_GET_ITEM(bases, 0);
meta = (PyObject *) (base0->ob_type);
}
Py_INCREF(meta);
isclass = 1; /* meta is really a class */
}
if (isclass) {
/* meta is really a class, so check for a more de...
metaclass, or possible metaclass conflicts: */
winner = (PyObject *)_PyType_CalculateMetaclass((...
b...
if (winner != meta) {
Py_DECREF(meta);
meta = winner;
Py_INCREF(meta);
}
}
/* else: meta is not a class, so we cannot do the met...
calculation, so we will use the explicitly given o...
}}
まずはここまで。kwdsがNULLじゃないときというのは「class F...
今回はメタクラスは指定しておらず、またスーパークラスも指...
*クラス定義コード実行(属性辞書の作成) [#f5c7c1c7]
#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_GetAttrId (Objects/object.c) [#j865bb14]
まず_PyObject_GetAttrId。これはPyObject_GetAttrを呼んでる...
#code(C){{
PyObject *
_PyObject_GetAttrId(PyObject *v, _Py_Identifier *name)
{
PyObject *result;
PyObject *oname = _PyUnicode_FromId(name); /* borrowe...
if (!oname)
return NULL;
result = PyObject_GetAttr(v, oname);
return result;
}
}}
続いてPyObject_GetAttr
#code(C){{
PyObject *
PyObject_GetAttr(PyObject *v, PyObject *name)
{
PyTypeObject *tp = Py_TYPE(v);
if (!PyUnicode_Check(name)) {
PyErr_Format(PyExc_TypeError,
"attribute name must be string, not ...
name->ob_type->tp_name);
return NULL;
}
if (tp->tp_getattro != NULL)
return (*tp->tp_getattro)(v, name);
if (tp->tp_getattr != NULL) {
char *name_str = PyUnicode_AsUTF8(name);
if (name_str == NULL)
return NULL;
return (*tp->tp_getattr)(v, name_str);
}
PyErr_Format(PyExc_AttributeError,
"'%.50s' object has no attribute '%U'",
tp->tp_name, name);
return NULL;
}
}}
はい、TypeObjectが出てきました。さらにそのメンバの関数ポ...
ここでType_Typeの定義(Objects/typeobject.c)を見てみる。
#code(C){{
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
// 省略
(getattrofunc)type_getattro, /* tp_get...
(setattrofunc)type_setattro, /* tp_set...
}}
というわけで、type_getattroが呼び出されることになります。
#code(C){{
static PyObject *
type_getattro(PyTypeObject *type, PyObject *name)
{
PyTypeObject *metatype = Py_TYPE(type);
PyObject *meta_attribute, *attribute;
descrgetfunc meta_get;
/* Initialize this type (we'll assume the metatype is...
if (type->tp_dict == NULL) {
if (PyType_Ready(type) < 0)
return NULL;
}
/* No readable descriptor found yet */
meta_get = NULL;
/* Look for the attribute in the metatype */
meta_attribute = _PyType_Lookup(metatype, name);
if (meta_attribute != NULL) {
// デスクリプタの処理。省略
Py_INCREF(meta_attribute);
}
/* No data descriptor found on metatype. Look in tp_d...
* type and its bases */
attribute = _PyType_Lookup(type, name);
if (attribute != NULL) {
/* Implement descriptor functionality, if any */
descrgetfunc local_get = Py_TYPE(attribute)->tp_d...
Py_XDECREF(meta_attribute);
// デスクリプタの処理。省略
Py_INCREF(attribute);
return attribute;
}
// 今回のケースではここまでで返ってると思うので省略
}
}}
今の場合、typeはType_Typeが渡されているので、TYPEはType_T...
_PyType_Lookupは基本的にはtp_dictから指定された属性を取得...
** PyType_Ready (Objects/typeobject.c) [#e8aab319]
さて、追っかけないといけない方の話をしましょう。PyType_Re...
なお、実際には_Py_InitializeEx_Privateの中で_Py_ReadyType...
PyType_Readyは長いので、__prepare__がtp_dictに登録される...
#code(C){{
static PyObject *
type_prepare(PyObject *self, PyObject *args, PyObject *kw...
{
return PyDict_New();
}
static PyMethodDef type_methods[] = {
// 省略
{"__prepare__", (PyCFunction)type_prepare,
METH_VARARGS | METH_KEYWORDS | METH_CLASS,
PyDoc_STR("__prepare__() -> dict\n"
"used to create the namespace for the clas...
// 省略
{0}
};
}}
PyMethodDefは前にbultinでも出てきましたね。type_methodsは...
#code(C){{
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
// 省略
type_members, /* tp_mem...
type_members, /* tp_mem...
type_getsets, /* tp_get...
}}
で、PyType_Ready中の登録してそうなところ
#code(C){{
/* Initialize tp_dict */
dict = type->tp_dict;
if (dict == NULL) {
dict = PyDict_New();
if (dict == NULL)
goto error;
type->tp_dict = dict;
}
/* Add type-specific descriptors to tp_dict */
if (add_operators(type) < 0)
goto error;
if (type->tp_methods != NULL) {
if (add_methods(type, type->tp_methods) < 0)
goto error;
}
if (type->tp_members != NULL) {
if (add_members(type, type->tp_members) < 0)
goto error;
}
if (type->tp_getset != NULL) {
if (add_getset(type, type->tp_getset) < 0)
goto error;
}
}}
add_methodsに進む。なお、add_operatorはTypeObjectに直接書...
#code(C){{
static int
add_methods(PyTypeObject *type, PyMethodDef *meth)
{
PyObject *dict = type->tp_dict;
for (; meth->ml_name != NULL; meth++) {
PyObject *descr;
int err;
if (PyDict_GetItemString(dict, meth->ml_name) && ...
continue;
if (meth->ml_flags & METH_CLASS) {
descr = PyDescr_NewClassMethod(type, meth);
}
else if (meth->ml_flags & METH_STATIC) {
// 省略
}
else {
descr = PyDescr_NewMethod(type, meth);
}
if (descr == NULL)
return -1;
err = PyDict_SetItemString(dict, meth->ml_name, d...
Py_DECREF(descr);
if (err < 0)
return -1;
}
return 0;
}
}}
あれ?デスクリプタ?先ほどtype_getattroでデスクリプタの場...
#code(C){{
PyObject *
PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *m...
{
PyMethodDescrObject *descr;
descr = (PyMethodDescrObject *)descr_new(&PyClassMeth...
type, method...
if (descr != NULL)
descr->d_method = method;
return (PyObject *)descr;
}
}}
**type_getattro再び [#xb78dfc0]
では読み違えていたところに戻りましょう。
#code(C){{
static PyObject *
type_getattro(PyTypeObject *type, PyObject *name)
{
// 省略
if (meta_attribute != NULL) {
meta_get = Py_TYPE(meta_attribute)->tp_descr_get;
if (meta_get != NULL && PyDescr_IsData(meta_attri...
/* Data descriptors implement tp_descr_set to...
* writes. Assume the attribute is not overri...
* type's tp_dict (and bases): call the descr...
*/
return meta_get(meta_attribute, (PyObject *)t...
(PyObject *)metatype);
}
Py_INCREF(meta_attribute);
}
/* No data descriptor found on metatype. Look in tp_d...
* type and its bases */
attribute = _PyType_Lookup(type, name);
if (attribute != NULL) {
/* Implement descriptor functionality, if any */
descrgetfunc local_get = Py_TYPE(attribute)->tp_d...
Py_XDECREF(meta_attribute);
if (local_get != NULL) {
/* NULL 2nd argument indicates the descriptor...
* found on the target object itself (or a ba...
return local_get(attribute, (PyObject *)NULL,
(PyObject *)type);
}
Py_INCREF(attribute);
return attribute;
}
}}
前述したように今はtypeもmetatype同じになりますが、metaの...
というわけでdescr_getとして呼び出されるclassmethod_get。...
#code(C){{
static PyObject *
classmethod_get(PyMethodDescrObject *descr, PyObject *obj...
{
return PyCFunction_NewEx(descr->d_method, type, NULL);
}
}}
ここでCFunctionObjectにラップされるか。
回り道が長引いたので一旦ここで区切ります。
*おわりに [#b9ab35bb]
今回は__build_class__、というよりCでのオブジェクト実装の...
また、PyTypeObjectに直接書かれている特殊メソッドおよび通...
ページ名: