Python2.6のC拡張モジュールを使ってオブジェクトをCで書いてみた(3)
前回までにオブジェクトにメソッドを追加する以外のことはやったので、ここではメソッドを追加してあげましょう。テストはtestStringに書いてありました。コメントアウトされてる部分を元に戻します。
def testString(self): s = String.new('abc') self.assertEqual(3, s.length) self.assertEqual('abc', s.c_str()) self.assertEqual('returns string', s.c_str.__doc__)#追加
c_str()なんてメソッドは追加してませんから、string_getattrがエラーを出します。Cの方を変更しましょう。
struct String { PyObject_VAR_HEAD int length; //文字列の要素を入れます。メモリを確保するときに、sizeof(struct String)+要素数の //sizeを要求することで、elementsは配列のように扱えます。 char elements[1]; }; //c_strメソッドの定義 static PyObject* string_c_str(struct String* self) { return Py_BuildValue("s", &self->elements[0]); } // Stringオブジェクトのメソッド static PyMethodDef string_methods[] = { // メソッド名, 関数定義, 引数のタイプ, __doc__属性 {"c_str", (PyCFunction)string_c_str, METH_NOARGS, "returns string"}, {NULL, NULL, 0, NULL}//番兵 }; static PyObject* string_getattr(struct String* self, char* name) { PyObject* res; //string_methodsからメソッドを探す res = Py_FindMethod(string_methods, (PyObject*)self, name); if(res) return res; if(!strcmp("length", name)) return Py_BuildValue("i", self->length); PyErr_SetString(PyExc_AttributeError, name); return NULL; } ... //elementsフィールドがないときは、要素のサイズは0だったのを修正する static PyTypeObject String_Type = { PyObject_HEAD_INIT(NULL) 0, "MyString", // object name sizeof(struct String), // size sizeof(char), // 要素のsizeはcharにする (destructor)string_dealloc, //tp_dealloc 0, //tp_print (getattrfunc)string_getattr //tp_getattr }; static PyObject* new(PyObject* self, PyObject* args) { char* s; struct String* str; int len; if(!PyArg_ParseTuple(args, "s#", &s, &len)) return NULL; //PyObject_NEW_VARの最後の引数はelementsの要素数を指定する str = PyObject_NEW_VAR(struct String,&String_Type, len + 1); str->length = len; //elementsフィールドに文字列をコピーする strcpy(&str->elements[0],s); return (PyObject*)str; }
これで全てのテストをパスします。これでcで書いたプログラムのラッパーを書いたり、オブジェクトを作ることができます。お疲れ様でした。(最新のpythonを見るとAPIはかなり変わってます。3系から変わったのかと思いますが未確認です)