2016-09-06 90 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