为包含swigobj的Python对象创建一个copy类方法

2024-03-29 00:47:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我创建了一个Python类,它的属性是Swig对象(恰好是C结构的包装器)。我希望能够创建该类的副本,例如,通过定义一个__copy__方法,该方法包含Swig对象的独立副本(使用copy模块copy类只创建一个指向原始对象的指针,deepcopy失败)。在

有人知道您是否可以在Python中复制内存块,并使用它来复制包含Swig对象的属性吗?或者,我可以在创建Swig对象的Swig接口文件中创建一个__copy____deepcopy__方法,它可以使用Csmemcpy?在


Tags: 模块文件对象方法内存属性定义副本
1条回答
网友
1楼 · 发布于 2024-03-29 00:47:45

从查看在LAL的Swig接口中实现的__deepcopy__,找到用于分配和释放内存的Swigmacros,以及我自己的(!)example关于将Swig接口扩展到C结构,我已经找到了如何为Swig包装结构创建__deepcopy__方法。在

重复我的gist,并扩展它以添加__deepcopy__方法,如下所示:

假设您有一些C代码包含这样的结构:

/* testswig.h file */
#include <stdlib.h>
#include <stdio.h>

typedef struct tagteststruct{
  double *data;
  size_t len;
} teststruct;

teststruct *CreateStruct(size_t len);

其中结构将包含一个data长度为len的数组。函数CreateStruct()分配 用于结构实例化的内存,定义为

^{pr2}$

如果您用SWIG包装它以便在python中使用,那么有一些类似python列表的方法可以用于, e、 g.从data数组中添加或获取项。为此,可以创建以下SWIG接口文件:

/* testswig.i */

%module testswig

%include "exception.i"

%{
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "testswig.h"
static int teststructErr = 0; // flag to save test struct error state
%}

%include "testswig.h"

// set exception handling for __getitem__
%exception tagteststruct::__getitem__ {
  assert(!teststructErr);
  $action
  if ( teststructErr ){
    teststructErr = 0; // clear flag for next time
    SWIG_exception(SWIG_IndexError, "Index out of bounds");
  }
}

// set exception handling for __setitem__
%exception tagteststruct::__setitem__ {
  assert(!teststructErr);
  $action
  if ( teststructErr ){
    teststructErr = 0; // clear flag for next time
    SWIG_exception(SWIG_IndexError, "Index out of bounds");
  }
}

// set exception handling for insert()
%exception tagteststruct::insert {
  assert(!teststructErr);
  $action
  if ( teststructErr ){
    teststructErr = 0; // clear flag for next time
    SWIG_exception(SWIG_IndexError, "Index out of bounds");
  }
}

// "extend" the structure with various methods
%extend tagteststruct{
  // add a __getitem__ method to the structure to get values from the data array
  double __getitem__(size_t i) {
    if (i >= $self->len) {
      teststructErr = 1;
      return 0;
    }
    return $self->data[i];
  }

  // add a __setitem__ method to the structure to set values in the data array
  void __setitem__(size_t i, double value) {
    if ( i >= $self->len ){
      teststructErr = 1;
      return;
    }
    $self->data[i] = value;  
  }


  size_t __len__(){
    return $self->len;
  }

  void insert(size_t i, double value) {
    if ( i >= $self->len ){
      teststructErr = 1;
      return;
    }
    $self->data[i] = value;
  }

  %typemap(in, noblock=1) const void *memo "";
  struct tagteststruct * __deepcopy__(const void *memo) {
    // copy structure
    struct tagteststruct * scopy = %new_copy(*$self, struct tagteststruct);
    // copy array within the structure
    scopy->data = %new_copy_array($self->data, $self->len, double);
    return scopy;
  }
  %clear const void *memo;
}

在上面的示例中,它将以下方法添加到结构中:

  • __getitem__:这允许像python中的列表项一样访问结构的data数组,例如,使用x[0]返回teststruct->data[0]中的值
  • __setitem__:这允许将结构的data数组值设置为python中的列表项,例如,使用x[0] = 1.2设置{}中的值
  • __len__:这将返回使用len(x)data数组的长度
  • insert():这将一个值插入数组中的特定索引中,就像__getitem__
  • __deepcopy__:这允许使用^{}创建结构的副本。在

这个例子还展示了如何对这些方法执行一些异常检查,特别是确保请求的索引不超过数组的大小。在

要编译和使用此示例,可以执行以下操作(参见SWIG的tutorial):

^{4}$

其中,-I/usr/include/python2.7标志指向包含Python.h文件的路径。这个 testswig_wrap.c文件由swig命令生成。在

然后可以在python中使用该结构,如下例所示:

>>> from testswig import CreateStruct
>>> # create an instance of the structure with 10 elements
>>> x = CreateStruct(10)
>>> # set the 5th element of the data array to 1.3
>>> x[4] = 1.3
>>> # output the 5th element of the array
>>> print(x[4])
1.3
>>> # output the length of the array
>>> print(len(x))
10
>>> # create a copy
>>> import copy
>>> y = copy.deepcopy(x)
>>> print(len(y))
10
>>> print(y[4])
1.3
>>> y[4] = 3.4
>>> print(y[4])
3.4
>>> print(x[4])  # check x hasn't been altered
1.3

Swig包装的结构本身可以在一个类中,例如:

from testswig import CreateStruct

class mystruct():
    def __init__(self, size):
        self.array = CreateStruct(size)
        self.name = 'array'

    def __len__(self):
        return len(self.array)

    def __getitem__(self, idx):
        return self.array[idx]

    def __setitem__(self, idx, val):
        self.array[idx] = val

我们可以测试:

>>> x = mystruct(10)
>>> x[4] = 1.2
>>> print(x[4])
1.2
>>> import copy
>>> y = copy.deepcopy(x)
>>> print(y[4])
1.2
>>> y[4] = 3.4
>>> print(y[4])
3.4
>>> print(x[4])  # check it hasn't changed
1.2

相关问题 更多 >