如何通过Python C API判断decimal.Decimal类型并调用函数完成转换
问题1:检测对象是否为decimal.Decimal类型
不需要依赖decimal模块未导出的内部宏/类型定义,最优的兼容方案是通过Python公共API导入Decimal类型后做类型校验,具体实现逻辑:
- 在你的扩展模块初始化阶段,导入decimal模块并获取Decimal类的引用,存储为全局静态变量:
static PyObject *global_decimal_type = NULL; // 模块初始化函数中添加如下逻辑 PyObject *decimal_module = PyImport_ImportModule("decimal"); if (decimal_module == NULL) { // 处理导入失败的情况,比如模块不存在时抛导入错误 return NULL; } global_decimal_type = PyObject_GetAttrString(decimal_module, "Decimal"); Py_DECREF(decimal_module); if (global_decimal_type == NULL) { return NULL; } // 注意要持有类型对象的引用,避免被GC回收 Py_INCREF(global_decimal_type);
- 类型检测时直接调用
PyObject_IsInstance即可:
int is_decimal = PyObject_IsInstance(value, global_decimal_type); if (is_decimal < 0) { // 处理检测过程中抛出的异常 return NULL; } if (is_decimal) { // 是Decimal类型的逻辑 }
这个方案完全依赖Python公共API,兼容所有支持decimal模块的Python版本,不会因为CPython内部实现变更失效。
问题2:可调用对象的调用
你找到的PyObject_CallObject是正确的API,str、float本身就是Python层面的可调用对象,之前调用失败大概率是参数处理或者异常校验遗漏导致的,完整的调用逻辑需要补充如下注意点:
- 调用前先校验传入的func是否可调用,避免后续调用出错:
if (!PyCallable_Check(func)) { PyErr_SetString(PyExc_TypeError, "第二个参数必须是可调用对象"); return NULL; }
- 调用时注意内存管理,
PyTuple_Pack返回的是新引用,调用完成后需要手动释放,避免内存泄漏:
PyObject *args = PyTuple_Pack(1, value); if (args == NULL) { return NULL; } PyObject *result = PyObject_CallObject(func, args); Py_DECREF(args); // 释放参数元组 if (result == NULL) { // 处理调用过程中抛出的异常 return NULL; } // 后续处理result逻辑
不需要改为传字符串参数,直接传入str、float或者用户自定义的单参数可调用对象都可以通过上述逻辑正常执行。
内容的提问来源于stack exchange,提问作者Bryant
相关产品推荐
相关产品推荐

