Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

Pythonクイズの答え

http://d.hatena.ne.jp/mopemope/20081017/p1

>>> import types
>>> class A(object):pass
...
>>> def test(self):
... print "test"
...
>>> a = A()
>>> a.test = types.MethodType(test, a, a.__class__)
>>> a.test
>
>>> a.test()
test
>>> b = A()
>>> b.test()
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'A' object has no attribute 'test'
>>>

newモジュールってあったよね、ってのを思い出して書いてみました。
newモジュールは3.0でなくなってしまうのでtypesを使う方を回答としています。
newモジュールを使用しているライブラリにはsetuptoolsなどがあります。
id:jbkingさん、id:morchinさん、id:odzさん回答ありがとうございました。

typeオブジェクト

typeオブジェクトはそのtypeのインスタンスを生成するファクトリとして機能します。

>>> import types
>>> types.IntType

>>> i = types.IntType(1)
>>> i
1
>>> type(i)

>>>

ClassType, UnboundMethodType,InstanceType,MethodTypeなどは

types.py
class _C: 
    def _m(self): pass
ClassType = type(_C)
UnboundMethodType = type(_C._m)         # Same as MethodType
_x = _C()
InstanceType = type(_x)
MethodType = type(_x._m)

のようになっています。
しかしこの状態だとイマイチなんで引数が(関数、インスタンス、クラス)なのかがわかりませんね。

typeはどこに?

実際これらtypeからインスタンス生成を生成するコードはCで書かれています。
pythonのオブジェクトシステム関連のソースはpythonのsrcのObject以下に入っています。
pythonの拡張モジュールを書いた事がある人は知っているかも知れませんがPyTypeObject
を定義する時に

  0,          /* tp_methods */
  0,          /* tp_members */
  0,          /* tp_getset */
  0,          /* tp_base */
  0,          /* tp_dict */
  0,          /* tp_descr_get */
  0,          /* tp_descr_set */
  0,          /* tp_dictoffset */
  0,          /* tp_init */
  0,          /* tp_alloc */
  instance_new,       /* tp_new */

type関連の関数をセットします。
tp_newの箇所がそのtypeを生成する関数になります。

インスタンスメソッドであるMethodTypeは以下のようになってます。

python2.6のObject/classobject.c
2610 PyTypeObject PyMethod_Type = {
2611   PyObject_HEAD_INIT(&PyType_Type)
2612   0,
2613   "instancemethod",
2614   sizeof(PyMethodObject),
2615   0,
2616   (destructor)instancemethod_dealloc, /* tp_dealloc */
2617   0,          /* tp_print */
2618   0,          /* tp_getattr */
2619   0,          /* tp_setattr */
2620   (cmpfunc)instancemethod_compare,  /* tp_compare */
2621   (reprfunc)instancemethod_repr,    /* tp_repr */
2622   0,          /* tp_as_number */
2623   0,          /* tp_as_sequence */
2624   0,          /* tp_as_mapping */
2625   (hashfunc)instancemethod_hash,    /* tp_hash */
2626   instancemethod_call,      /* tp_call */
2627   0,          /* tp_str */
2628   instancemethod_getattro,    /* tp_getattro */
2629   PyObject_GenericSetAttr,    /* tp_setattro */
2630   0,          /* tp_as_buffer */
2631   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC  | Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */
2632   instancemethod_doc,     /* tp_doc */
2633   (traverseproc)instancemethod_traverse,  /* tp_traverse */
2634   0,          /* tp_clear */
2635   0,          /* tp_richcompare */
2636   offsetof(PyMethodObject, im_weakreflist), /* tp_weaklistoffset */
2637   0,          /* tp_iter */
2638   0,          /* tp_iternext */
2639   0,          /* tp_methods */
2640   instancemethod_memberlist,    /* tp_members */
2641   instancemethod_getset,      /* tp_getset */
2642   0,          /* tp_base */
2643   0,          /* tp_dict */
2644   instancemethod_descr_get,   /* tp_descr_get */
2645   0,          /* tp_descr_set */
2646   0,          /* tp_dictoffset */
2647   0,          /* tp_init */
2648   0,          /* tp_alloc */
2649   instancemethod_new,     /* tp_new */
2650 };

tp_newはinstancemethod_newとなっています。

2330 static PyObject *
2331 instancemethod_new(PyTypeObject* type, PyObject* args, PyObject *kw)
2332 {
2333   PyObject *func;
2334   PyObject *self;
2335   PyObject *classObj = NULL;
2336 
2337   if (!_PyArg_NoKeywords("instancemethod", kw))
2338     return NULL;
2339   if (!PyArg_UnpackTuple(args, "instancemethod", 2, 3,
2340             &func, &self, &classObj))
2341     return NULL;
2342   if (!PyCallable_Check(func)) {
2343     PyErr_SetString(PyExc_TypeError,
2344         "first argument must be callable");
2345     return NULL;
2346   }
2347   if (self == Py_None)
2348     self = NULL;
2349   if (self == NULL && classObj == NULL) {
2350     PyErr_SetString(PyExc_TypeError,
2351       "unbound methods must have non-NULL im_class");
2352     return NULL;
2353   }
2354 
2355   return PyMethod_New(func, self, classObj);
2356 }

instancemethod_newはPyMethod_Newを呼び出します。

2225 PyObject *
2226 PyMethod_New(PyObject *func, PyObject *self, PyObject *klass)
2227 {
2228   register PyMethodObject *im;
2229   if (!PyCallable_Check(func)) {
2230     PyErr_BadInternalCall();
2231     return NULL;
2232   }

ここまでくると引数に何を渡しているのかわかりますね。

ちなみにpython3.0では最後のclassは渡さなくてもよくなります。

 42 PyObject *
 43 PyMethod_New(PyObject *func, PyObject *self)
 44 {
 45   register PyMethodObject *im;
 46   if (!PyCallable_Check(func)) {

そもそもpython3.0ではクラス周りに色々手が入っているようです。


type、結合メソッド、非結合メソッドなど以下の書籍に詳しく書かれているので読んでみるといいかも知れません。

Pythonクィックリファレンス

Pythonクィックリファレンス