如何在C++ __getitem__函数中处理切片(供SWIG使用)

2 投票
1 回答
1205 浏览
提问于 2025-04-17 06:47

我正在为一个扩展了std::vector的C++类开发Python绑定。为了让这个类在Python中支持下标操作,我添加了一个__getitem__函数,代码大致如下(我省略了不相关的代码和错误处理部分):

class Column;

typedef vector<Column*> MetaDataBase;

class MetaData : public MetaDataBase {
public:

#ifdef SWIGPYTHON
Column* __getitem__(int i) { return (*this)[i]; }
#endif
};

这个代码在Python中可以正常访问单个元素,但对于切片操作就不行了。

好的,我明白我需要把函数的参数类型改成PyObject *,并使用PySlice_Check来检查这个函数是否应该返回一个PyList

这样做没问题。但因为有时候我需要从这个函数返回PyList,所以__getitem__的返回值类型也必须是PyObject*,而我不能依赖SWIG来把我的C++类型(Column *)转换成一个包装类。此外,在创建切片的时候,我需要“手动”把Column*转换成PyObject*,然后再插入到PyList中。

我该怎么做呢?

1 个回答

3

我觉得对于std::vector,用SWIG和Python可以有一个更简单的解决方案。SWIG生成的Python代码已经很好地支持了一些标准模板库(STL)容器。

如果你在模块接口的开头加上:

%include "pyabc.i"
%include "std_vector.i" // Assuming you don't already

然后在某个地方,如果你还没有这样做:

%template(MetaDataBase) std::vector<Column*>;

这样做会让包装后的std::vector符合Python的可变序列的要求。(我觉得这应该足够让你在Python那边实现你想要的功能,可能还需要在调用SWIG时加上-extranative选项)。

另外值得注意的是,对于你当前的__getitem__,你可以在SWIG接口文件中用类似下面的方式声明和定义:

%extend MetaData {
   Column* __getitem__(int i) { return (*self)[i]; }
};

这样可以让你在不“污染”你“正常”的头文件的情况下,添加SWIG和Python特定的代码。

撰写回答