Swig无法将numpy.float64转为C++ double引发TypeError求助
问题场景
用SWIG实现Python与C模块绑定,新增C函数后,带@numpy.vectorize装饰的函数在输入numpy.float64类型数组时,调用新函数触发TypeError。测试发现vectorize会先以numpy.float64类型传入数组首个元素(SWIG默认无法转为C++ double),后续元素则以Python float类型传入,旧C++函数可正常处理。
测试代码:
import numpy as np @np.vectorize def test(mu): print(mu, type(mu)) test(np.array([6.2, 67.3]))
输出结果:
6.2 <class 'numpy.float64'> 6.2 <class 'float'> 67.3 <class 'float'>
业务代码问题示例:
import numpy as np import cpplib @np.vectorize def eps(t, mu): if t > 0: return cpplib.old_func(mu) # 正常运行 else: return cpplib.new_func(mu) # 触发TypeError
新旧C函数均为接收double、返回double的签名,头文件、wrap.i及SWIG生成的包装代码未发现明显差异。尝试过NumPy文档建议的复制pyfragments.swg到项目目录,但之前无需此操作且复制后引发编译错误;此前也曾出现该问题,同事无法复现且自行恢复,修复C语法错误无效。
解决方案
1. 给vectorize指定otypes参数强制输出类型
numpy.vectorize的首次类型检测可能因新函数返回值推断异常,明确指定输出类型可让vectorize统一处理输入类型转换:
@np.vectorize(otypes=[np.float64]) def eps(t, mu): if t > 0: return cpplib.old_func(mu) else: return cpplib.new_func(mu)
2. 在SWIG接口文件中显式添加类型映射
在wrap.i中添加SWIG类型映射,让其支持numpy.float64到C++ double的转换:
%include "numpy.i" %init %{ import_array(); %} // 显式映射numpy.float64到double %typemap(in) double { if (PyFloat_Check($input) || PyArray_CheckScalar($input, NPY_DOUBLE)) { $1 = PyFloat_AsDouble($input); } else { PyErr_SetString(PyExc_TypeError, "Expected a float or numpy.float64"); return NULL; } }
需先引入numpy.i并初始化numpy的C API(import_array()),确保SWIG能识别numpy标量类型。
3. Python层手动转换输入类型
在vectorize包裹的函数内,先将输入转为Python float再传入C++函数,快速规避类型转换问题:
@np.vectorize def eps(t, mu): mu_float = float(mu) if t > 0: return cpplib.old_func(mu_float) else: return cpplib.new_func(mu_float)
4. 排查SWIG生成的包装代码细节
对比旧函数与新函数的SWIG生成代码:
- 检查新函数的参数处理逻辑,是否缺失对numpy标量的判断
- 确认SWIG处理新函数时,是否正确应用了项目中已有的numpy类型映射配置
可能的根本原因
旧函数可能因历史配置、SWIG版本差异或缓存的包装代码,自动带上了numpy类型转换逻辑;而新函数未触发该逻辑。加上numpy.vectorize首次调用用numpy.float64做类型测试,新函数的SWIG包装不支持该类型,直接触发报错。
内容的提问来源于stack exchange,提问作者user29650973

