Вопрос

Я хочу имитировать часть кода C на 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), но это не масштабируется с более сложными структурами.Кроме того, это необходимо будет делать рекурсивно для полей не базового, а структурированного типа.

Мой другой вариант - использовать 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 здесь правильная операция.Установив 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>

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

Сейчас я также думаю об определении такого метода, как:

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

Но, возможно, есть еще более приятные варианты...

В 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 с атрибуция
Не связан с StackOverflow
scroll top