【问题标题】:Create a copy class method for a Python object containing a Swig object为包含 Swig 对象的 Python 对象创建复制类方法
【发布时间】:2019-07-31 12:24:19
【问题描述】:

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

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

【问题讨论】:

    标签: python copy swig memcpy


    【解决方案1】:

    从查看在 Swig 接口中为 LAL 实现的 __deepcopy__,找到用于分配和释放内存的 Swig macros,并查看我自己的(!)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);
    

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

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

    如果你用 SWIG 包装它以在 python 中使用,那么提供一些类似于 python 列表的方法可能会很有用, 例如,从 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__:这允许结构的data数组像python中的列表项一样被访问,例如,使用x[0]返回teststruct-&gt;data[0]中的值
    • __setitem__:这允许将结构的data 数组值设置为python 中的列表项,例如,使用x[0] = 1.2 设置teststruct-&gt;data[0] 中的值
    • __len__:使用len(x) 时返回data 数组的长度
    • insert():这会将一个值插入到数组中的特定索引中,就像 __getitem__
    • __deepcopy__:这允许使用deepcopy 创建结构的副本。

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

    要编译和使用此示例,您可以执行以下操作(例如,参见 SWIG 的 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
    

    在这种情况下,-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
    

    【讨论】:

      猜你喜欢
      • 2015-08-02
      • 1970-01-01
      • 1970-01-01
      • 2014-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-08
      相关资源
      最近更新 更多