2016-09-06 98 views
0

我正在使用一些封闭的Python模块:我可以通过API调用方法,但无法访问实现。我知道这个模块基本上包装了一些C++代码。Python:将SwigPythonObject转换为Python对象

所以其中一种方法的返回值类型是SwigPythonObject。我以后如何处理这个对象,假设我没有其他来自模块经销商的辅助工具和文档?

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

目前我在调试器中看到的是一样的东西:

{SwigPythonObject} _<hexa number>_p_unsigned_char 
+1

你可以直接“将其转换为常规python”。可能有一种黑客可以使用ctypes作为你显示的类型。 – Flexo

回答

1

这是一个有点不清楚你在问什么的语义,但基本上它好像你已经有了一个指向unsigned char来自SWIG,您希望与之合作。略猜有可能是3箱子你很可能会遇到这样的:

  1. 指针确实是指向一个无符号字节
  2. 的指针指向一个空结束的字符串。 (为什么它不仅仅是作为一个字符串包装?)
  3. 指针指向一个固定长度的无符号字节数组。 (你需要知道/猜测长度)

在这个特殊的情况下,因为没有包装或对齐来担心所有三种情况我们实际上可以为所有上述使用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是我们未知痛饮无符号的字符指针给我们的指针是在为一个整数指向的地址的值。结合ctype的from_address静态方法,我们可以构造ctypes实例来访问SWIG直接了解的内存。 (注:地址通过调用int()的字符串表示演出的地址,因为前者是数据的指向真实地址不符返回,但后者是SWIG 代理对象的地址)

可能最简单的包装是固定长度的情况 - 我们可以通过使用正确尺寸的c_ubyte上的*运算符创建一个ctypes类型,然后调用from_address

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

最后为指针单字节情况下,我基本上重复使用现有的ctypes的I型必须创建一个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