2009-09-24 13 views
9

voglio imitare un pezzo di codice C in Python con ctypes, il codice è qualcosa di simile:Python ctypes: il contenuto di Struttura copiare

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

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

in ctypes Non è possibile effettuare le seguenti operazioni:

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) 

come contents è ancora un oggetto Python ctypes Structure, gestito come riferimento stesso.

Una soluzione ovvia sarebbe quella di copiare manualmente ogni campo (che è rappresentato come immutable python int), ma che non scala con strutture più complesse. Inoltre, dovrebbe essere eseguito in modo ricorsivo per i campi che non sono di base, ma tipi strutturati.

La mia altra opzione è utilizzare memmove e copiare gli oggetti come se fossero buffer, ma questo sembra molto soggetto a errori (poichè Python è digitato dinamicamente sarebbe troppo facile usarlo con oggetti di tipo e dimensione distinti, che conducono alla memoria difetti di corruzione o segmentazione) ...

Qualche suggerimento?

Edit:

Potrei anche usare una nuova copia fresca della struttura, quindi forse questo potrebbe essere utile:

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

ma non so se ci potrebbe essere qualche tipo di comportamenti bizzarri copiando ctypes proxy come se fossero oggetti Python regolari ...

+1

Sfortunatamente 'deepcopy' ha esito negativo se la struttura ctypes contiene puntatori:' ValoreErrore: gli oggetti di tipo ctypes contenenti puntatori non possono essere sottoposti a pickled'. – 101

risposta

5

È possibile utilizzare l'assegnazione sequenza per copiare la punta-a oggetti (anziché assegnare a p.contents, che cambia il valore del puntatore):

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 farà tipo controllo per voi (che non è ingannare -proof, ma è meglio di niente).

Esempio di utilizzo, con la verifica che lo fa nel lavoro fatto;):

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

Sembra promettente, ma cambia solo il pointee: -s indirizzo di stampa di (src) e indirizzo di (dst.contents) dopo averlo assegnato per controllarlo. – fortran

+0

Queste funzioni non dovrebbero essere passate a puntatori, dovrebbero essere gli oggetti di struttura 'ctypes'. vuoi una funzione analoga al tuo C 'copy_point', fai' dst [0] = src [0] '. – Miles

+0

Hmmmm ... Non riesco a vedere perché il comportamento sia cambiato da doing' dst = pointer (a); dst [0] = src; 'a' pointer (a) [0] = src': - | – fortran

0

ora sto anche pensando di definire un metodo come:

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

Ma ci potrebbero essere le opzioni ancora più bello là fuori ...

+0

Si consigliano alcuni errori di ortografia ma si consiglia di controllare la sicurezza del tipo. – whatnick

0

operazioni puntatore come regola non sono sicuri molto memoria. Vorrei creare classi wrapper per ogni tipo di dati di struttura che ti interessa e lasciargli gestire le operazioni di copia puntatore. Praticamente come stai facendo qui. Ci sono funzioni lambda e mappe che puoi usare ricorsivamente come zucchero sintattico.

+2

what a void answer :-( – fortran

+0

Prezzo di pensare ad alta voce .. ci sono metaclassi che possono essere utilizzati un bel meccanismo di mappatura.http: //code.activestate.com/recipes/576666/ – whatnick

6

memmove è il corretto funzionamento qui. Impostando lo argtypes della funzione CopyPoint, è possibile applicare facilmente la sicurezza del 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 

uscite:

$ 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 che si potrebbe facilmente fare una fabbrica per generare questo tipo di funzione a tempo di esecuzione sulla base di un tipo specifico, se è necessario generalizzare:

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 

uscita:

<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

In python 3x, il tuo codice può funzionare correttamente. mostrato di seguito:

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