问题
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 C
s 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'sdata
array to be accessed like a list item in python, e.g., usingx[0]
returns the value inteststruct->data[0]
__setitem__
: this allows the structure'sdata
array values to be set like a list item in python, e.g., usingx[0] = 1.2
sets the value inteststruct->data[0]
__len__
: this returns the length of thedata
array when usinglen(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