Pregunta

Quiero imitar una pieza de código C en Python con ctypes, el código es algo como:

typedef struct {
  int x;
  int y;
} point;

void copy_point(point *a, point *b) {
  *a = *b;
}

ctypes en que no es posible hacer lo siguiente:

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)

como el contents todavía es una estructura de objetos ctypes Python, que se gestiona como una referencia a sí mismo.

Una solución obvia sería copiar manualmente cada campo (que se representa como inmutable pitón de int), pero eso no significa escala con estructuras más complejas. Además, tendría que ser hecho de forma recursiva para los campos que no son básicas, pero los tipos estructurados.

Mi otra opción es utilizar memmove y copiar los objetos como si fueran tampones, pero que parece muy propenso a errores (como Python se escribe dinámicamente sería demasiado fácil de usar con objetos de tipo y tamaño distinto, lo que lleva a la corrupción de memoria o fallos de segmentación) ...

¿Alguna sugerencia?

Editar

También podría usar una nueva copia nueva de la estructura, así que quizás esto podría ser útil:

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

pero no sé si podría haber algún tipo de comportamientos extraños copiando ctypes proxys como si fueran objetos de Python normal ...

¿Fue útil?

Solución

Puede utilizar asignación de secuencia para copiar al cual apunta a los objetos (en lugar de asignar a p.contents, que cambia el valor del puntero):

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 hará la comprobación de tipos para usted (que no es a prueba de tontos, pero es mejor que nada).

Ejemplo de uso, con la verificación de que lo hace en el trabajo hecho;):

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

Otros consejos

memmove es el correcto funcionamiento aquí. Al establecer el argtypes de su función CopyPoint, puede cumplir fácilmente tipo de seguridad.

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

salidas:

$ 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

Tenga en cuenta que usted podría hacer fácilmente una fábrica para generar este tipo de función en tiempo de ejecución basado en un tipo específico, si es necesario generalizar:

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

salida:

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

operaciones de puntero como regla general no son muy seguros de memoria. Me gustaría crear clases contenedoras para cada tipo de datos struct le interesa y dejar que ellos manejan las operaciones de copia de puntero. Más o menos como que está haciendo aquí. Hay lambda y mapa de funciones que se pueden utilizar de forma recursiva como el azúcar sintáctica.

Ahora estoy pensando también en la definición de un método como:

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

Pero puede haber opciones aún más agradables por ahí ...

En 3x pitón, su código puede funcionar correctamente. se muestra a continuación:

>>> 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top