2009-09-24 2 views
9

я хочу, чтобы имитировать кусок кода C в Python с 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 все еще является элементом типа Cython Python, который управляется как сама ссылка.

Очевидным обходным путем было бы вручную скопировать каждое поле (которое представлено как неизменяемое значение python int), но это не масштабируется с более сложными структурами. Кроме того, это нужно сделать рекурсивно для полей, которые не являются базовыми, но структурированными типами.

Моим другим вариантом является использование memmove и копирование объектов, как если бы они были буферами, но это кажется очень подверженным ошибкам (поскольку Python динамически вводится, было бы слишком легко использовать его с объектами различного типа и размера, к повреждениям памяти или сегментации) ...

Любые предложения?

Edit:

Я мог бы также использовать свежую копию структуры, так что, возможно, это может быть полезно:

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

, но я не знаю, что может быть каким-то причудливое поведение копирование ctypes Proxys, как если бы они были обычные объекты Python ...

+1

К сожалению, 'deepcopy' терпит неудачу, если структура ctypes содержит указатели:' ValueError: объекты ctypes, содержащие указатели, не могут быть маринованными'. – 101

ответ

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] 

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

Кажется перспективным, но он просто меняется pointee: -s print addressof (src) и addressof (dst.contents) после назначения для проверки. – fortran

+0

Эти функции не должны быть переданы указателями, они должны быть объектами структуры 'ctypes'. Если вы хотите функцию, аналогичную вашей C 'copy_point', do' dst [0] = src [0] '. – Miles

+0

Хмммм ... Я не понимаю, почему поведение изменилось с выполнения' dst = pointer (a); dst [0] = src; 'to' pointer (a) [0] = src': - | – fortran

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

0

Pointer операции, как правило, не очень памяти безопасно. Я бы создал классы-оболочки для каждого типа данных структуры, которые вам интересны, и пусть они обрабатывают операции копирования указателя. Очень похоже на то, что вы здесь делаете. Есть функции лямбда и карты, которые вы можете использовать рекурсивно в качестве синтаксического сахара.

+2

Что за пустота ответ :-( – fortran

+0

Цена мышления вслух .. есть метаклассы, которые можно использовать с хорошим механизмом отображения. Http: //code.activestate.com/recipes/576666/ – whatnick

6

memmove является правильной работой здесь. Установив argtypes вашей функции CopyPoint, вы можете легко обеспечить безопасность типов.

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