Python:将SwigPythonObject转换为Python对象

时间:2016-09-06 08:22:12

标签: python swig

我使用了一些封闭的Python模块:我可以通过API调用方法,但我无法访问实现。我知道这个模块基本上包含了一些C ++代码。

因此返回值类型之一是SwigPythonObject。我如何在以后使用此对象,假设我没有来自模块分发者或文档的任何其他帮助?

我想以某种方式将他转换为"常规" python对象并在调试器中观察他的内部成员结构。

目前我在调试器中看到的是:

{SwigPythonObject} _<hexa number>_p_unsigned_char

1 个答案:

答案 0 :(得分:3)

你要问的语义有点不清楚,但基本上你似乎有一个指向你希望使用的SWIG unsigned char的指针。稍微猜测一下,你可能会遇到3个案例:

  1. 指针实际上是指向单个无符号字节的指针
  2. 指针是指向以null结尾的字符串的指针。 (为什么不把它作为一个字符串包裹起来?)
  3. 指针指向固定长度的无符号字节数组。 (你需要以某种方式知道/猜测长度)
  4. 在这个特定的实例中,由于没有任何打包或对齐担心所有三种情况,我们实际上可以为所有上述情况编写一些内容,这些情况使用ctypes来读取SWIG直接引用到Python的内存,并执行SWIG代理。 (请注意,如果我们看到的类型比指向单个内置类型或数组的指针更复杂,那么我们在这里做不了多少)

    首先在C - test.h中编写一些代码来练习我们正在处理的内容:

    inline unsigned char *test_str() {
      static unsigned char data[] = "HELLO WORLD";
      return data;
    }
    
    inline unsigned char *test_byte() {
      static unsigned char val = 66;
      return &val;
    }
    

    接下来是一个包含此内容的最小SWIG模块:

    %module test
    
    %{
    #include "test.h"
    %}
    
    %include "test.h"
    

    我们可以在ipython中查看它,并看到它被包装(类似地) 你观察到了什么:

    In [1]: import test
    
    In [2]: test.test_byte()
    Out[2]: <Swig Object of type 'unsigned char *' at 0x7fc2851cbde0>
    
    In [3]: test.test_str()
    Out[3]: <Swig Object of type 'unsigned char *' at 0x7fc2851cbe70>
    
    In [4]: hex(int(test.test_str()))
    Out[4]: '0x7f905b0e72cd'
    

    我们在每种情况下使用的是调用int(x)的事实,其中x是我们未知的SWIG无符号字符指针,它给出了指针作为整数指向的地址的值。将其与ctype的from_address静态方法相结合,我们可以构造ctypes实例来访问SWIG直接知道的内存。 (注意:调用int()返回的地址与字符串表示中的地址不匹配,因为前者是指向的数据的实际地址,但后者是SWIG 代理的地址< / em> object)

    可能最简单的换行是固定长度的情况 - 我们可以使用正确大小的*上的c_ubyte运算符创建一个ctypes类型,然后调用from_address

    对于以null结尾的字符串情况,我们确实有两个选择:使用libc strlen函数来计算字符串长度,然后构造匹配的ctypes类型,或者只使用Python中的char循环char直到我们命中一个空。我在下面的例子中选择了后者,因为它更简单。我可能通过使用生成器和itertools.count()跟踪位置来过度复杂化。

    最后,对于指向单字节情况的指针,我基本上重用了现有的ctypes类型,我必须创建一个1字节的数组并读取其中的值。可能有一种方法可以使用ctypes.POINTER(ctypes.c_ubyte)然后.contents从地址构造一个类型,但是我无法快速看到它,所以使用1字节数组技巧对我来说是微不足道的。

    所有这些结合起来给我以下Python代码:

    import ctypes
    import test
    import itertools
    
    # Case 2
    def swig_to_str(s):
      base = int(s)
      ty = ctypes.c_ubyte*1
      def impl():
        for x in itertools.count():
          v=ty.from_address(base+x)[0]
          if not v: return
          yield chr(v)
      return ''.join(impl())
    
    # Case 1
    def swig_to_byte(b):
      ty=ctypes.c_ubyte*1
      v=ty.from_address(int(b))
      return v[0]
    
    # Case 3
    def swig_to_fixed_len(s, l):
      ty=ctypes.c_ubyte*l
      return ''.join(chr(x) for x in ty.from_address(int(s)))
    
    t=test.test_str()
    print(t)
    print(swig_to_str(t))
    print(swig_to_fixed_len(t,5))
    
    u=test.test_byte()
    print(u)
    print(swig_to_byte(u))
    

    这与Python 2.7的结果一样(应该花费很少的努力使其正确3):

    swig3.0 -python -Wall test.i
    gcc -std=gnu99 -Wall test_wrap.c -o  _test.so -shared -I/usr/include/python2.7/ -fPIC
    python run.py 
    
    <Swig Object of type 'unsigned char *' at 0x7f4a57581cf0>
    HELLO WORLD
    HELLO
    <Swig Object of type 'unsigned char *' at 0x7f4a57581de0>
    66