2009-09-24 21 views
9

que quiero imitar una pieza de código C en Python con ctypes, el código es algo como:ctypes Python: contenidos copia de Estructura

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

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

en ctypes 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 contents todavía es un objeto de estructura de Python ctypes, que se gestiona como una referencia.

Una solución obvia sería copiar manualmente cada campo (que se representa como int inmutable python), pero eso no escala con estructuras más complejas. Además, tendría que hacerse recursivamente para campos que no son básicos, sino tipos estructurados.

Mi otra opción es usar memmove y copiar los objetos como si fueran almacenamientos intermedios, pero eso parece muy propenso a errores (ya que Python está tipeado dinámicamente sería demasiado fácil de usar con objetos de distinto tipo y tamaño, a corrupción de memoria o fallas de segmentación) ...

¿Alguna sugerencia?

Editar:

También podría utilizar 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 regulares ...

+1

Desafortunadamente 'deepcopy' falla si la estructura de los tipos contiene punteros:' ValueError: los objetos de tipo ctypes que contienen punteros no se pueden conservar en escabeche'. – 101

Respuesta

5

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 va a hacer la comprobación de tipos para usted (que no se engañe a prueba, 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) 
+0

Parecía prometedor, pero solo cambia the pointee: -s print addressof (src) y addressof (dst.contents) después de asignar para verificarlo. – fortran

+0

Se supone que esas funciones no son punteros pasados, se supone que son los objetos de estructura 'ctypes'. quiere una función análoga a su C 'copy_point', do' dst [0] = src [0] '. – Miles

+0

Hmmmm ... No puedo ver por qué el comportamiento cambió de hacer' dst = puntero (a); dst [0] = src; 'to' puntero (a) [0] = src': - | – fortran

0

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

+0

Se recomiendan algunos errores ortográficos, pero se debe realizar una comprobación de seguridad de tipo. – whatnick

0

operaciones de puntero como regla general no son muy seguros de memoria. Crearía clases de contenedor para cada tipo de datos de estructura que le interese y les permitiría manejar las operaciones de copia del puntero. Casi como si estuvieras haciendo aquí. Hay funciones lambda y de mapas que puedes usar recursivamente como azúcar sintáctico.

+2

qué respuesta nula :-( – fortran

+0

Precio de pensar en voz alta ... hay metaclases que pueden usarse como un buen mecanismo de mapeo.http: //code.activestate.com/recipes/576666/ – whatnick

6

memmove es la operación correcta aquí. Al configurar el argtypes de su función de CopyPoint, puede aplicar fácilmente la seguridad de tipo.

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 

Nota que fácilmente se podría hacer 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 

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

En python 3x, su código puede ejecutarse 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 
Cuestiones relacionadas