Create a copy class method for a Python object containing a Swig object

喜欢而已 提交于 2021-02-08 09:18:30

问题


I have created a Python class with an attribute that is a Swig object (which happens to be a wrapper of a C structure). I want to be able to create copies of that class, e.g., by defining a __copy__ method, that contain independent copies of the Swig object (using the copy modules' copy class just creates a pointer to the original object, and deepcopy fails).

Does anyone know if you can just copy chunks of memory in Python, and use this to copy the attribute containing the Swig object? Or, could I create a __copy__ or __deepcopy__ method in the Swig interface file that created the Swig object, which is able to use Cs memcpy?


回答1:


From looking at the __deepcopy__ implemented in the Swig interface for LAL, finding the Swig macros for allocating and deallocating memory, and looking at my own(!) example of extending the Swig interface to a C structure, I have figured out how to create a __deepcopy__ method for the Swig-wrapped structure.

Repeating my gist, and extending it to add a __deepcopy__ method is as follows:

Say you have some C code containing a structure like this:

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

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

teststruct *CreateStruct(size_t len);

where the structure will contain a data array of length len. The function CreateStruct() allocates memory for an instantiation of the structure, and is defined as

/* testswig.c file */
#include "testswig.h"

/* function for allocating memory for test struct */
teststruct *CreateStruct(size_t len){
  teststruct *ts = NULL;

  ts = (teststruct *)malloc(sizeof(teststruct));
  ts->data = (double *)malloc(sizeof(double)*len);
  ts->len = len;
  return ts;
}

If you wrap this with SWIG for use in python, then it might be useful to have some python list-like methods available to, e.g., add or get items from the data array. To do this you can create the following SWIG interface file:

/* 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;
}

In the above example, it adds the following methods to the structure:

  • __getitem__: this allows the structure's data array to be accessed like a list item in python, e.g., using x[0] returns the value in teststruct->data[0]
  • __setitem__: this allows the structure's data array values to be set like a list item in python, e.g., using x[0] = 1.2 sets the value in teststruct->data[0]
  • __len__: this returns the length of the data array when using len(x)
  • insert(): this inserts a value into a particular index in the array like with __getitem__
  • __deepcopy__: this allows the use of deepcopy to create a copy of the structure.

The example also shows how to perform some exception checking for these methods, in particular, making sure the requested index does not exceed the size of the array.

To compile and use this example, you could do the following (see, e.g., SWIG's tutorial):

$ swig -python testswig.i
$ gcc -c testswig.c testswig_wrap.c -fPIC -I/usr/include/python2.7
$ ld -shared testswig.o testswig_wrap.o -o _testswig.so

where, in this case, the -I/usr/include/python2.7 flag points to the path containing the Python.h file. The testswig_wrap.c file is generated by the swig command.

The structure can then be used in python as in the following example:

>>> 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

The Swig-wrapped structure could itself be in a class, e.g.,:

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

which we can test:

>>> 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


来源:https://stackoverflow.com/questions/57290678/create-a-copy-class-method-for-a-python-object-containing-a-swig-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!