代码之家  ›  专栏  ›  技术社区  ›  Fire Lancer

python c-api使len(…)与扩展类一起工作

  •  4
  • Fire Lancer  · 技术社区  · 14 年前

    在Python中创建类时,我只需 def __len__(self): 方法使 len(InstanceOfMyClass) 但是,我不知道如何通过C-API使用扩展类来实现这一点。

    我试图添加 __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);
    }
    
    2 回复  |  直到 14 年前
        1
  •  3
  •   Luper Rouch François Lagunas    14 年前

    正如Ignacio所说,正确的方法是 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__ 在模块的init函数中使用 PyCFunction_New , PyMethod_New PyObject_SetAttr . 这看起来更像您粘贴的代码,除了您定义的 爱伦尼亚 在C方法表中,这不起作用。

        2
  •  3
  •   Ignacio Vazquez-Abrams    14 年前

    这个 tp_as_sequence typeobject结构的成员应填充具有 sq_length 成员已填充。