如何在Python的C扩展中设置静态类变量?
在Python C扩展中设置静态类变量的方法
你说得太对了!在Python的C扩展API里,PyTypeObject确实没给静态类变量专门留插槽——官方文档里列的那些插槽基本都是管实例的行为、成员这些的。不过别慌,有两种非常实用的办法能搞定这个需求:
方法一:在类初始化完成后添加静态变量
当我们调用PyType_Ready()完成自定义类型的初始化后,就可以直接给类对象本身添加属性,这些属性就会成为静态类变量。
举个具体的代码例子,假设我们要实现一个名为MyClass的Python类,并给它添加一个静态变量static_counter:
#include <Python.h> // 定义实例结构体 typedef struct { PyObject_HEAD // 实例成员 int instance_val; } MyClassObject; // 定义类型对象 static PyTypeObject MyClassType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "myextension.MyClass", .tp_basicsize = sizeof(MyClassObject), .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = "My custom class", // 这里可以添加其他插槽,比如tp_init、tp_dealloc等 }; // 模块定义 static struct PyModuleDef myextensionmodule = { PyModuleDef_HEAD_INIT, .m_name = "myextension", .m_doc = "Example module with static class variable", .m_size = -1, }; PyMODINIT_FUNC PyInit_myextension(void) { PyObject* m; // 初始化类型 if (PyType_Ready(&MyClassType) < 0) return NULL; m = PyModule_Create(&myextensionmodule); if (m == NULL) return NULL; // 将类添加到模块 Py_INCREF(&MyClassType); if (PyModule_AddObject(m, "MyClass", (PyObject*)&MyClassType) < 0) { Py_DECREF(&MyClassType); Py_DECREF(m); return NULL; } // 给类添加静态变量static_counter,值为42 PyObject* static_val = PyLong_FromLong(42); if (static_val == NULL) { Py_DECREF(m); return NULL; } // 设置类属性 if (PyObject_SetAttrString((PyObject*)&MyClassType, "static_counter", static_val) < 0) { Py_DECREF(static_val); Py_DECREF(m); return NULL; } // 因为PyObject_SetAttrString会增加引用,所以这里要释放我们创建的对象 Py_DECREF(static_val); return m; }
这种方法的好处是简单直观,适合在模块初始化时动态添加静态变量。
方法二:预先填充类型的tp_dict
我们也可以在定义PyTypeObject时,预先创建一个字典并赋值给tp_dict字段,字典里的键值对就会成为类的静态变量。不过这种方式需要注意内存管理,确保字典的引用计数正确。
修改上面的例子,用这种方式实现:
#include <Python.h> // 定义实例结构体 typedef struct { PyObject_HEAD int instance_val; } MyClassObject; // 预先创建静态变量字典 static PyObject* my_class_dict; // 定义类型对象 static PyTypeObject MyClassType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "myextension.MyClass", .tp_basicsize = sizeof(MyClassObject), .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = "My custom class", .tp_dict = NULL, // 先初始化为NULL,后面再赋值 }; // 模块定义 static struct PyModuleDef myextensionmodule = { PyModuleDef_HEAD_INIT, .m_name = "myextension", .m_doc = "Example module with static class variable", .m_size = -1, }; PyMODINIT_FUNC PyInit_myextension(void) { PyObject* m; // 创建静态变量字典 my_class_dict = PyDict_New(); if (my_class_dict == NULL) return NULL; // 向字典添加静态变量static_counter PyObject* static_val = PyLong_FromLong(42); if (static_val == NULL) { Py_DECREF(my_class_dict); return NULL; } if (PyDict_SetItemString(my_class_dict, "static_counter", static_val) < 0) { Py_DECREF(static_val); Py_DECREF(my_class_dict); return NULL; } Py_DECREF(static_val); // 将字典赋值给类型的tp_dict MyClassType.tp_dict = my_class_dict; // 初始化类型 if (PyType_Ready(&MyClassType) < 0) { Py_DECREF(my_class_dict); return NULL; } m = PyModule_Create(&myextensionmodule); if (m == NULL) { Py_DECREF(my_class_dict); return NULL; } // 添加类到模块 Py_INCREF(&MyClassType); if (PyModule_AddObject(m, "MyClass", (PyObject*)&MyClassType) < 0) { Py_DECREF(&MyClassType); Py_DECREF(my_class_dict); Py_DECREF(m); return NULL; } return m; }
需要注意的是,tp_dict会在PyType_Ready()被调用时被类型系统接管,所以我们不需要在后续手动释放它(除非初始化失败)。
验证效果
编译这个扩展模块后,在Python里就可以像普通类一样访问静态变量了:
import myextension print(myextension.MyClass.static_counter) # 输出42
两种方法都能实现静态类变量的需求,你可以根据自己的场景选择——如果需要动态添加(比如根据模块参数调整),选第一种;如果变量是固定的,预先定义字典的方式也很稳妥。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

