我想用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 Structure对象,它本身作为引用进行管理。
一个明显的解决方法是手动复制每个字段(表示为不可变的python int),但这不会扩展到更复杂的结构。此外,对于非基本字段但结构化类型的字段,需要递归完成。
我的另一个选择是使用memmove
并将对象复制为缓冲区,但这似乎非常容易出错(因为Python是动态类型的,因此很容易将它与不同类型的对象一起使用大小,导致内存损坏或分段错误)...
有什么建议吗?
修改:
我也可以使用结构的全新副本,所以这可能有用:
import copy
p0 = Point()
p1 = copy.deepcopy(p0) #or just a shallow copy for this example
但我不知道是否有某种奇怪的行为复制ctypes代理,好像它们是普通的Python对象......
答案 0 :(得分: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)
答案 1 :(得分: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>
答案 2 :(得分: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))
但可能还有更好的选择...
答案 3 :(得分:0)
作为规则的指针操作不是非常安全的内存。我将为您感兴趣的每个struct数据类型创建包装类,并让它们处理指针复制操作。就像你在这里做的一样。有lambda和map函数,你可以递归地使用它们作为语法糖。
答案 4 :(得分: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