2010-01-14 55 views
4

在Python中創建一個類時,我可以簡單地使def __len__(self):方法使len(InstanceOfMyClass)工作,但我無法找到如何做到這一點一個通過C-API的擴展類。Python的C-API使len(...)與擴展類一起工作

我嘗試添加一個__len__方法,但似乎行不通

{"__len__",(PyCFunction)&TestClass_GetLen,METH_NOARGS,""}, 

的Python測試代碼:

def test(my_instance): 
    x = len(my_instance)#exception 
    return x 

TypeError: object of type 'test_module.test_class' has no len() 

代碼的TestClass

struct TestClass; 
static int TestClass_Init(TestClass *self, PyObject *args, PyObject* kwds); 
static void TestClass_Dealloc(TestClass *self); 
static PyObject* TestClass_GetLen(TestClass *self); 

struct TestClass 
{ 
    PyObject_HEAD; 
}; 
static PyMethodDef TestClass_methods[] = 
{ 
    {"__len__",(PyCFunction)&TestClass_GetLen,METH_O,""}, 
    {NULL} 
}; 
static PyTypeObject TestClass_type = {PyObject_HEAD_INIT(NULL)}; 
bool InitTestClass(PyObject *module) 
{ 
    TestClass_type.tp_basicsize = sizeof(TestClass); 
    TestClass_type.tp_name  = PY_MODULE_NAME".TestClass"; 
    TestClass_type.tp_doc  = ""; 
    TestClass_type.tp_flags  = Py_TPFLAGS_DEFAULT; 
    TestClass_type.tp_methods = TestClass_methods; 
    TestClass_type.tp_new  = PyType_GenericNew; 
    TestClass_type.tp_init  = (initproc)TestClass_Init; 
    TestClass_type.tp_dealloc = (destructor)TestClass_Dealloc; 
    if(PyType_Ready(TestClass_type) < 0) return false; 

    Py_INCREF(TestClass_type); 
    PyModule_AddObject(module, "TestClass", (PyObject*)&TestClass_type); 
    return true; 
}; 
void TestClass_Dealloc(TestClass *self) 
{ 
    Py_TYPE(self)->tp_free((PyObject*)self); 
} 
int TestClass_Init(TestClass *self, PyObject *args, PyObject* kwds) 
{ 
    return 0; 
} 
PyObject* TestClass_GetLen(TestClass *self) 
{ 
    return PyLong_FromLong(55); 
} 

回答

3

由於Igniacio稱,正確的方法是填寫您的typeobject的tp_as_sequence成員。下面是一個小例子:

#include <Python.h> 

/** 
* C structure and methods definitions 
*/ 

typedef struct { 
    PyObject_HEAD; 
} TestObject; 

static Py_ssize_t 
TestClass_len(TestObject* self) 
{ 
    return 55; 
} 

/** 
* Python definitions 
*/ 

static PySequenceMethods TestClass_sequence_methods = { 
    TestClass_len,     /* sq_length */ 
}; 

static PyTypeObject TestClass = { 
    PyObject_HEAD_INIT(NULL)  
    0,        /*ob_size*/ 
    "testmodule.TestClass",   /*tp_name*/ 
    sizeof(TestObject),    /*tp_basicsize*/ 
}; 

/** 
* Module entry point 
*/ 
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 
#define PyMODINIT_FUNC void 
#endif 
PyMODINIT_FUNC 
inittestmodule(void) 
{ 
    PyObject* m; 

    TestClass.tp_new = PyType_GenericNew; 
    TestClass.tp_as_sequence = &TestClass_sequence_methods; 
    TestClass.tp_flags = Py_TPFLAGS_DEFAULT; 

    if (PyType_Ready(&TestClass) < 0) 
     return; 

    m = Py_InitModule3("testmodule", NULL, ""); 

    Py_INCREF(&TestClass); 
    PyModule_AddObject(m, "TestClass", (PyObject *)&TestClass); 
} 

另一個(更復雜和更慢)的方式是一個__len__方法在模塊的初始化函數動態添加到您的類PyCFunction_NewPyMethod_NewPyObject_SetAttr。這看起來更像是你粘貼的代碼,只不過你在C方法表中定義了__len__,這是行不通的。

+0

「類型錯誤:類型'test_module.test_class'的對象沒有len()」與「{」__len __「,(PyCFunction)&TestClass_GetLen,METH_O,」「}」:( – 2010-01-14 14:50:42

+0

可以粘貼完整的C代碼嗎? – 2010-01-14 16:01:19

+0

好吧,我添加了TestClass的所有代碼 – 2010-01-16 11:06:28

3

該typeobject結構的tp_as_sequence成員應該填入填充其sq_length成員的內容。

+0

確實,它在http://docs.python.org/a-api中有記錄/typeobj.html#tp_as_sequence。 – 2010-01-16 11:24:35