Question

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

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

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

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 contents est encore un ctypes python objet de structure, qui est gérée comme une référence lui-même.

Une solution évidente serait de copier manuellement chaque champ (qui est représenté comme immuable de python int), mais n'échelle avec des structures plus complexes. En outre, il faudrait faire récursive pour les champs qui ne sont pas de base, mais aussi des types structurés.

Mon autre option consiste à utiliser memmove et copier les objets comme si elles étaient des tampons, mais qui semble très sujette aux erreurs (comme Python est typé dynamiquement, il serait trop facile de l'utiliser avec des objets de type distinct et la taille, ce qui conduit à défauts corruption de mémoire ou segmentation) ...

Toutes les 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 de comportements bizarres copiaient ctypes proxys comme si elles étaient des objets Python ordinaire ...

Était-ce utile?

La solution

Vous pouvez utiliser l'attribution de séquence pour copier les objets à fait (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 (ce qui est pas infaillible, mais il vaut 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)

Autres conseils

memmove est le bon fonctionnement ici. En définissant la argtypes de votre fonction CopyPoint, vous pouvez facilement faire respecter la sécurité-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 produire 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>

opérations de pointeur comme règle ne sont pas très sûre mémoire. Je voudrais créer des classes wrapper pour chaque type de données struct vous intéresse et laisser gérer les opérations de copie de pointeur. Un peu comme vous faites ici. Il y a des fonctions lambda et carte que vous pouvez utiliser récursive comme le sucre syntaxique.

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 encore plus agréables options là-bas ...

En 3x python, votre code peut fonctionner 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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top