2009-09-24 48 views
9

我想模仿一塊在Python的C代碼的ctypes,代碼是一樣的東西:的Python ctypes的:複製結構的內容

typedef struct { 
    int x; 
    int y; 
} point; 

void copy_point(point *a, point *b) { 
    *a = *b; 
} 

在ctypes的這是不可能做到以下幾點:

from ctypes import * 

class Point(Structure): 
    _fields_ = [("x", c_int),("y", c_int)] 

def copy_point(a, b): 
    a.contents = b.contents 

p0 = pointer(Point()) 
p1 = pointer(Point()) 
copy_point(p0,p1) 

作爲contents仍然是一個Python ctypes結構對象,它作爲一個引用自身進行管理。

一個顯而易見的解決方法是手動複製每個字段(表示爲不可變的python int),但不能用更復雜的結構進行擴展。此外,它需要遞歸完成不是基本的,但結構化類型的字段。

我的其他選擇是使用memmove並將對象複製爲緩衝區,但似乎非常容易出錯(因爲Python是動態類型的,所以它很容易與不同類型和大小的對象一起使用,導致到內存損壞或分段錯誤)...

有什麼建議嗎?

編輯

我還可以使用該結構的一個全新的副本,所以也許這可能是有用的:

import copy 
p0 = Point() 
p1 = copy.deepcopy(p0) #or just a shallow copy for this example 

,但我不知道是否有可能是某種離奇的行爲複製的ctypes proxys,好像他們是常規的Python對象...

+1

不幸'deepcopy'失敗如果ctypes的結構包含指針:'ValueError異常:ctypes的包含指針對象不能被pickled'。 – 101 2015-04-02 00:18:03

回答

5

您可以使用順序分配給指向的拷貝對象(而不是分配給p.contents,這會改變指針值):

def copy(dst, src): 
    """Copies the contents of src to dst""" 
    pointer(dst)[0] = src 

# alternately 
def new_copy(src): 
    """Returns a new ctypes object which is a bitwise copy of an existing one""" 
    dst = type(src)() 
    pointer(dst)[0] = src 
    return dst 

# or if using pointers 
def ptr_copy(dst_ptr, src_ptr): 
    dst_ptr[0] = src_ptr[0] 

​​會做類型檢查你(這是騙不了但它比沒有好)。使用

例如,覈實,它實際是工作):

>>> o1 = Point(1, 1) 
>>> o2 = Point(2, 2) 
>>> print (o1.x, o1.y, addressof(o1)), (o2.x, o2.y, addressof(o2)) 
(1, 1, 6474004) (2, 2, 6473524) 
>>> copy(o2, o1) 
>>> pprint (o1.x, o1.y, addressof(o1)), (o2.x, o2.y, addressof(o2)) 
(1, 1, 6474004) (1, 1, 6473524) 

>>> o1 = Point(1, 1), o2 = Point(2, 2) 
>>> print (o1.x, o1.y, addressof(o1)), (o2.x, o2.y, addressof(o2)) 
(1, 1, 6473844) (2, 2, 6473684) 
>>> p1, p2 = pointer(o1), pointer(o2) 
>>> addressof(p1.contents), addressof(p2.contents) 
(6473844, 6473684) 
>>> ptr_copy(p1, p2) 
>>> print (o1.x, o1.y, addressof(o1)), (o2.x, o2.y, addressof(o2)) 
(2, 2, 6473844) (2, 2, 6473684) 
>>> addressof(p1.contents), addressof(p2.contents) 
(6473844, 6473684) 
+0

看起來很有前途,但它只是改變指針:-s打印addressof(src)和addressof(dst.contents)在分配後檢查它 – fortran 2009-09-24 09:16:48

+0

這些函數不應該被傳遞指針,它們應該是'ctypes'結構對象。想要一個類似於C'copy_point'的函數,做'dst [0] = src [0]'。 – Miles 2009-09-24 09:24:59

+0

嗯......我看不出爲什麼行爲從做'dst = pointer(a); dst [0] = src;'to'指針(a)[0] = src': - | – fortran 2009-09-24 09:49:28

0

現在我還想着定義等的方法:

def safe_copy(dst, src): 
    if type(src) != type(dst) or not isinstance(src, Structure): 
    raise Exception("wrong types") 
    memmove(addressof(dst), addressof(src), sizeof(src)) 

但有可能是更好的還是選擇在那裏...

+0

建議使用一些拼寫錯誤,但輸入安全檢查。 – whatnick 2009-09-24 08:21:18

0

指針操作的規則都不是很安全的內存。我將爲您感興趣的每種結構數據類型創建包裝類,並讓它們處理指針複製操作。非常像你在這裏做。有lambda和map函數可以遞歸地用作語法糖。

+2

什麼是無效答案:-( – fortran 2009-09-24 08:35:57

+0

價格大聲思索..有metaclasses可以使用一個很好的映射機制.http://code.activestate.com/recipes/576666/ – whatnick 2009-09-24 09:04:44

6

memmove是這裏的正確操作。通過設置CopyPoint功能的argtypes,您可以輕鬆實施類型安全。

from ctypes import * 

class Point(Structure): 
    _fields_ = [("x", c_int), ("y", c_int)] 
    def __str__(self): 
     return "<Point: x=%d, y=%d, addr=%ld>" % (self.x, self.y, addressof(self)) 

def CopyPoint(a, b): 
    memmove(a, b, sizeof(Point)) 
CopyPoint.argtypes = [POINTER(Point), POINTER(Point)] 

pt0 = Point(x=0, y=10) 
pt1 = Point(x=5, y=7) 

print pt0, pt1 

CopyPoint(byref(pt0), byref(pt1)) 
print pt0, pt1  

try: 
    CopyPoint(byref(pt0), Point(x=2, y=3)) 
except ArgumentError as e: 
    print "Could not copy!", e 

輸出:

$ python ct.py 
<Point: x=0, y=10, addr=3083711192> <Point: x=5, y=7, addr=3083711120> 
<Point: x=5, y=7, addr=3083711192> <Point: x=5, y=7, addr=3083711120> 
Could not copy! argument 2: <type 'exceptions.TypeError'>: wrong type 

需要注意的是,你可以很容易地使一家工廠在運行時根據特定類型產生這種功能,如果你需要概括:

def CopierFactory(typ): 
    def f(a,b): 
     memmove(a,b, sizeof(typ)) 
    f.argtypes = [POINTER(typ), POINTER(typ)] 

    return f 

copy_point = CopierFactory(Point) 

a = Point(x=1, y=2) 
b = Point(x=-1, y=-1) 
print a, b 
copy_point(byref(a), byref(b)) 
print a, b 

輸出:

<Point: x=1, y=2, addr=3085088024> <Point: x=-1, y=-1, addr=3085087952> 
<Point: x=-1, y=-1, addr=3085088024> <Point: x=-1, y=-1, addr=3085087952> 
0

在python 3x中,您的代碼可以正確運行。如下圖所示:

>>> from ctypes import * 
>>> class Point(Structure): 
... _fields_ = [("x", c_int),("y", c_int)] 
>>> def copy_point(a, b): 
... a.contents = b.contents 
>>> p0 = pointer(Point()) 
>>> p1 = pointer(Point(1,2)) 
>>> p0.contents.x 
0 
>>> copy_point(p0,p1) 
>>> p0.contents.x 
1