Python:将SwigPythonObject转换为Python对象

2024-10-08 19:25:02 发布

您现在位置:Python中文网/ 问答频道 /正文

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

所以其中一个方法返回值类型是SwigPythonObject。假设我没有来自模块分发程序或文档的任何其他帮助,我以后如何处理这个对象?在

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

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

{SwigPythonObject} _<hexa number>_p_unsigned_char

Tags: 模块对象方法代码文档程序api类型
1条回答
网友
1楼 · 发布于 2024-10-08 19:25:02

您所问内容的语义有点不清楚,但基本上似乎您有一个指向SWIG的unsigned char的指针,您想使用它。稍微猜测一下,您可能会在以下三种情况下遇到这种情况:

  1. 指针实际上是指向单个无符号字节的指针
  2. 指针是指向以null结尾的字符串的指针。(为什么不把它包装成一根绳子?)在
  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模块,它封装了:

^{pr2}$

我们可以在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()返回的地址与字符串表示中显示的地址不匹配,因为前者是指向的数据的真实地址,而后者是SWIGproxy对象的地址)

最简单的包装方法可能是固定长度的大小写—我们可以通过对大小合适的*运算符创建一个ctypes类型,然后调用from_address。在

对于以null结尾的字符串,我们有两个选择:要么使用libc strlen函数计算字符串长度,然后构造一个匹配的ctypes类型,要么只从Python中逐个字符地循环,直到达到null为止。我在下面的例子中选择了后者,因为它更简单。不过,我可能用生成器和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))

这在Python2.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

相关问题 更多 >

    热门问题