让C++对象数组在Python中可迭代

4 投票
3 回答
3003 浏览
提问于 2025-04-17 10:26

我在网上搜索过,但没有找到解决办法。我把下面的示例代码用SWIG包装成了Python:

class atomo {
public:
    int i;
    atomo(int a) {
        i = a;
    };      
};

class funa {
public:
    atomo *lista[3];

    funa() {
        lista[0] = new atomo(1);
        lista[1] = new atomo(2);
        lista[2] = new atomo(3);
    };
};

但是Python无法通过以下命令遍历或访问lista

>>> test = myModule.funa()
>>> test.lista[0]
      Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in __iter__
      TypeError: 'SwigPyObject' object is not subscriptable

>>> for i in test.lista:
>>>     print(i)
      Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in __iter__
      TypeError: 'SwigPyObject' object is not subscriptable

我该如何让lista可以被遍历呢?有没有办法用Python的列表代替C++的数组?

我的Python版本是3.2,使用的是SWIG 2.0.4和g++ 4.6.1。

谢谢!

3 个回答

0

一种和 larsmans 类似的方法是让 Funa.__iter__ 返回一个生成器对象。这样的话,你只需要在 SWIG 创建的接口上添加一些内容就可以了。(如果按照他的方式,你就得把其他每个方法都包一遍,或者得去调整 __getattr__。)大致上可以这样做:

class Funa:

  class FunaIter :
    def __init__(self, parent) :
      self.parent = parent
      self.pos = 0

    def __iter__(self) :
      while self.pos < 3 :
        yield self.parent.lista[self.pos]
        self.pos += 1

  def __iter__(self) :
    return self.FunaIter(self)

这样插入到你的 SWIG 文件里会更简单,可以使用 %extend%pythoncode 指令。

另外,SWIG 对 STL 容器有封装,所以也许你可以利用这些,轻松获取所需的项目获取器。

1

为了简单起见,你可能想在Python这边解决这个问题,而不是在C++/SWIG那边。

# wrapper/facade
class Funa:
    def __init__(self):
        self._impl = myModule.funa()   # _impl => implementation

    def __iter__(self):
        for i in xrange(3):
            yield self._impl.lista[i]

test = Funa()
for x in test:
    print(x)
3

从你的问题来看,似乎不太清楚你是想使用 std::vector 还是自己定义的数组。

如果你选择 std::vector,可以参考一些类似于 C++ 的代码:

#include <vector>
#include <string>

struct foo {
  std::string name;
};

inline std::vector<foo> test() {
  std::vector<foo> ret;
  foo instance;
  instance.name = "one";
  ret.push_back(instance);
  instance.name = "two";
  ret.push_back(instance);
  return ret;
}

你可以用 %templatepyabc.istd_vector.i 来包装它,比如:

%module test

%{
#include "test.h"
%}

%include "pyabc.i"
%include "std_vector.i"

%include "test.h"

%template (FooVector) std::vector<foo>;

这样在 Python 中使用时就会比较直观。你需要用类似下面的命令来调用 SWIG:

swig -python -c++ -py3 -extranative test.i

如果你的想法是包装一个“自定义”的容器,让它在 Python 中使用时也能很自然,我在之前的回答中提供了一个 详细示例

撰写回答