2016-05-03 50 views
2

我正在寫一個包裝外部C庫的Python C擴展。在原有的庫有(討論的緣故T型)結構,所以我的擴展類看起來是這樣的:如何從Python C擴展中訪問子類的對象結構體字段?

typedef struct { 
    PyObject_HEAD 
    T *cdata; 
} TWrapperBase; 

我還需要從時間查找指針在Python中的時間,所以我暴露了一個只讀字段_cdata,這是一個cdata指針作爲unsigned long long(是的,我知道它不是很便攜,但現在它已經超出了範圍)。

然後,我希望能夠增加在Python一些方法,但我不能只將它們附加在C聲明的類,所以我繼承它並添加我的新方法:

class TWrapper(TWrapperBase): 
    ... 

現在,在我的C擴展代碼中,我需要一個訪問cdata字段的方法,所以我可以將它傳遞給庫函數。我知道self不會是TWrapperBase的實例,而是TWrapper(此Python版本)。 這樣做的正確方法是什麼?

static PyObject * doStuff(PyObject *self) 
{ 
    T *cdata_ptr; 
    // How to get a pointer to cdata? 
    // 
    // This looks very unsafe to me, do I have any guarantee of 
    // the subclass memory layout? 
    // 1. cdata_ptr = ((TWrapperBase*)self)->cdata 
    // 
    // This is probably safe, but it seems to be a bit of a hassle 
    // to query it with a string key 
    // 2. cdata_ptr = PyLong_AsVoidPtr(PyObject_GetAttrString(self, "_cdata")) 
    do_important_library_stuff(cdata_ptr); 
    Py_INCREF(self); 
    return self; 
} 

謝謝!

+0

你爲什麼在做'Py_INCREF(self);'*之後*你做圖書館的東西? –

+0

這就是爲什麼選項1對你來說看起來不安全? –

+0

我是'INCREF'ing它只是因爲它是一個返回類型和返回的對象是被盜的引用。 – wesolyromek

回答

2
// This looks very unsafe to me, do I have any guarantee of 
    // the subclass memory layout? 
    // 1. cdata_ptr = ((TWrapperBase*)self)->cdata 

是的,那是有效的。你可以看看Python's built-in types的所有實現,並看到它們幾乎完全相同,通常不檢查它們是否在子類實例上運行。

+0

真的嗎?那麼它是否總是這樣呢,一個子類中所需的字段和其他東西在它的基類的數據之後被放置在內存中?這是一些標準的一部分,還是Python總是如何實現的? – wesolyromek

+0

@wesolyromek:這是CPython實現的工作原理。請注意,在Python中定義的「fields」通常是'__dict__'條目,而不是您在C中定義的結構字段類型,所以Python級別的子類通常不會對其超類的內存佈局做很多工作。 (如果你使用'__slots__',那就改變了一切。) – user2357112

+0

所以(只要我不使用'__slots__')它就像這樣工作:所有對象的佈局都以它們的C基類所擁有的開始,然後只有一些這些字典和其他字段的指針被修改爲獲取子類,對吧? – wesolyromek