Python ctypes:复制结构体内容
我想用ctypes在Python中模仿一段C代码,代码大概是这样的:
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中是不可变的整数),但如果结构体变得复杂,这种方法就不太好用了。而且,对于那些不是基本类型而是结构类型的字段,还需要递归地进行复制。
我的另一个选择是使用memmove
,把对象当作缓冲区来复制,但这似乎很容易出错(因为Python是动态类型的,使用不同类型和大小的对象很容易导致内存损坏或段错误)……
有没有什么建议呢?
编辑:
我也可以使用一个全新的结构体副本,所以也许这会有用:
import copy
p0 = Point()
p1 = copy.deepcopy(p0) #or just a shallow copy for this example
但我不知道把ctypes代理当作普通Python对象复制时,会不会出现一些奇怪的行为……
5 个回答
1
我现在也在考虑定义一个像这样的函数:
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))
不过可能还有更好的选择...
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>
6
你可以使用序列赋值来复制指向的对象(而不是直接给 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]
ctypes
会帮你进行类型检查(虽然不是绝对可靠,但总比没有好)。
下面是一个使用的例子,并且验证它确实有效;):
>>> 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)