繁体   English   中英

Python ctypes:复制Structure的内容

[英]Python ctypes: copying Structure's contents

我想用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 int),但这不会扩展到更复杂的结构。 此外,对于非基本字段而非结构化类型,需要递归地完成。

我的另一个选择是使用memmove并将对象复制,就像它们是缓冲区一样,但这似乎非常容易出错(因为Python是动态类型的,因此很容易将它与不同类型和大小的对象一起使用,导致内存损坏或分段错误)...

有什么建议?

编辑

我也可以使用一个全新的结构副本,所以这可能有用:

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

但我不知道是否有某种奇怪的行为复制ctypes代理,好像它们是普通的Python对象......

您可以使用序列分配来复制指向的对象(而不是分配给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)

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>

我现在也在考虑定义一个方法,如:

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))

但那里可能还有更好的选择......

作为规则的指针操作不是非常安全的内存。 我将为您感兴趣的每个struct数据类型创建包装类,并让它们处理指针复制操作。 就像你在这里做的一样。 有lambda和map函数,你可以递归地使用它们作为语法糖。

在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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM