2009-09-24 11 views
9

Je veux imiter un morceau de code C en Python avec ctypes, le code est quelque chose comme:Python ctypes: le contenu de la copie Structure

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

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

dans ctypes il est impossible de faire ce qui suit:

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) 

comme le contents est toujours un objet structure Python ctypes, qui est géré comme une référence elle-même.

Une solution de contournement évidente consisterait à copier manuellement chaque champ (qui est représenté par un int de python immuable), mais cela ne s'applique pas aux structures plus complexes. En outre, il devrait être fait récursivement pour les champs qui ne sont pas de base, mais les types structurés.

Mon autre option est d'utiliser memmove et de copier les objets comme s'ils étaient des tampons, mais cela semble très sujet aux erreurs (comme Python est dynamiquement typé, il serait trop facile de l'utiliser avec des objets de type et de taille distincts, à la mémoire de la corruption ou des fautes de segmentation) ...

Des suggestions?

Modifier:

Je pourrais aussi utiliser une nouvelle nouvelle copie de la structure, alors peut-être cela pourrait être utile:

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

mais je ne sais pas s'il pourrait y avoir une sorte des comportements bizarres copiaient ctypes proxys comme si elles étaient des objets Python réguliers ...

+1

Malheureusement, 'deepcopy' échoue si la structure ctypes contient des pointeurs:' ValueError: les objets ctypes contenant des pointeurs ne peuvent pas être décapés'. – 101

Répondre

5

Vous pouvez utiliser l'attribution de séquence pour copier l'objets pointés (plutôt que d'attribuer à p.contents, ce qui modifie la valeur du pointeur):

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 fera la vérification de type pour vous (qui est fou pas -proof, mais c'est mieux que rien).

Exemple d'utilisation, avec vérification qu'il fait dans le travail de fait;):

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

Semblait prometteur, mais il change juste la pointe: -s imprime addressof (src) et addressof (dst.contents) après l'avoir assignée pour le vérifier – fortran

+0

Ces fonctions ne sont pas censées être des pointeurs passés, elles sont supposées être les objets de structure 'ctypes'. voulez une fonction analogue à votre 'copy_point' C, faites' dst [0] = src [0] '. – Miles

+0

Hmmmm ... Je ne vois pas pourquoi le comportement a changé de faire' dst = pointeur (a); dst [0] = src; 'to' pointer (a) [0] = src': - | – fortran

0

Je suis maintenant penser à la définition d'une méthode comme:

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

Mais il pourrait y avoir des options encore plus agréable là-bas ...

+0

Quelques fautes d'orthographe mais une vérification de sécurité de type est recommandée. – whatnick

0

opérations de pointeur comme règle ne sont pas très mémoire en toute sécurité. Je créer des classes wrapper pour chaque type de données struct qui vous intéresse et les laisser gérer les opérations de copie de pointeur. Assez comme vous faites ici. Il existe des fonctions lambda et carte que vous pouvez utiliser récursivement comme sucre syntaxique.

+2

qu'est-ce qu'une réponse vide :-( – fortran

+0

Prix de la pensée à haute voix .. il existe des métaclasses qui peuvent être utilisés un bon mécanisme de cartographie.http: //code.activestate.com/recipes/576666/ – whatnick

6

memmove est le bon fonctionnement ici. En définissant le argtypes de votre fonction CopyPoint, vous pouvez facilement appliquer la sécurité de type.

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 

sorties:

$ 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 

Notez que vous pouvez facilement faire une usine pour générer ce genre de fonction lors de l'exécution en fonction d'un type particulier, si vous avez besoin de généraliser:

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 

sortie:

<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, votre code peut s'exécuter correctement. ci-dessous:

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