我为一个C库编写了一个Python扩展。我的数据结构如下所示:
typedef struct _mystruct{
double * clientdata;
size_t len;
} MyStruct;
这个数据类型的用途直接映射到Python中的list数据类型。因此,我想为导出的结构创建“list-like”行为,以便使用C扩展编写的代码更加“python”。在
特别是,这是我希望能够做到的(从python代码) 注意:py_ctsruct是在python中访问的ctsruct数据类型。在
我的要求可以概括为:
根据PEP234,如果一个对象实现了 _iter或<getitem。使用这个逻辑,我认为通过将以下属性(通过rename)添加到我的SWIG接口文件中,我将获得所需的行为(除了req。#1以上-我仍然不知道如何实现):
^{pr2}$现在我可以用python索引C对象了。我还没有实现Python异常抛出,但是如果超过数组边界,则返回一个幻数(错误代码)。在
有趣的是,当我尝试使用“for x in”语法迭代结构时,例如:
for i in py_cstruct:
print i
Python进入一个无限循环,在控制台上简单地打印上面提到的神奇(错误)数字。这对我来说意味着索引有问题。在
最后但并非最不重要的是,我如何实现需求1?这包括(据我所知):
[[更新]]
我很有兴趣看到一些代码片段,说明我需要在接口文件中放入什么(如果有的话),这样我就可以从Python迭代c结构的元素。在
最简单的解决方案是实现^{} 并对无效索引抛出一个^{} 异常。在
我举了一个例子,在SWIG中使用}来实现
%extend
和{__getitem__
,并分别引发一个异常:我通过添加到test.h:
^{pr2}$并运行以下Python:
哪个打印:
然后结束。在
另一种方法是,使用typemap将
MyStruct
直接映射到PyList
上,这也是可能的:这将创建一个
PyList
,其中包含任何返回MyStruct *
的函数的返回值。我用与前面方法完全相同的函数测试了这个%typemap(out)
。在您还可以编写一个相应的
%typemap(in)
和%typemap(freearg)
,类似于以下未测试的代码:对于链表这样的容器,使用迭代器会更有意义,但为了完整起见,下面是如何使用},在本例中,
__iter__
来实现MyStruct
。关键是让SWIG为您包装另一个类型,它提供所需的__iter__()
和{MyStructIter
同时使用%inline
进行定义和包装,因为它不是普通C API的一部分:对iteration over containers的要求是容器需要实现
__iter__()
并返回一个新的迭代器,但是除了next()
返回下一个项并增加迭代器之外,迭代器本身还必须提供__iter__()
方法。这意味着容器或迭代器可以相同地使用。在MyStructIter
需要跟踪迭代的当前状态—我们在哪里,还有剩下多少。在这个例子中,我保持一个指向下一个项目的指针和一个计数器,当我们到达末尾时,我们用它来判断。您还可以通过保持一个指向迭代器使用的MyStruct
的指针和该位置的计数器来跟踪状态,如下所示:(在本例中,我们实际上可以将容器本身用作迭代器,方法是提供一个返回容器的副本的
__iter__()
和类似于第一个类型的next()
。在我最初的回答中我没有这样做,因为我认为这比有两种不同的类型(容器和容器的迭代器)更不清楚我在python2.6中遇到了同样的问题,并通过@aphex reply解决了这个问题。 但我想避免任何魔术值,或额外的布尔值来传递列表末尾条件。果然,我的迭代器有一个atEnd()方法,它告诉我已经超过了列表的末尾。在
所以实际上,使用SWIG异常处理是相当容易的。我只需要添加以下魔法:
关键是,一旦超过列表末尾,snipet将完全跳过next()调用。在
如果你坚持你的习惯用法,它应该看起来像:
^{pr2}$python3.x注意事项:
您应该用神奇的“yu”前缀和后缀名来命名next()函数。一种选择是简单地添加:
对于我正在处理的接口,我使用了一个类对象,它有一些方法来访问代码中的数据。这些方法是用C++编写的。然后,在“I”文件内部的类中使用了%python代码指令,并在Python代码中创建了“<强> GestEng/St>”和“<强> SeTiTEM>强”>方法,使用了EngultC++方法使其看起来像字典式访问。在
相关问题 更多 >
编程相关推荐