2009-09-24 16 views
9

chcę naśladować kawałek kod C w Pythonie z ctypes, kod jest coś takiego:Python ctypes: Treść Kopiowanie struktura jest

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

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

w ctypes nie jest możliwe, aby wykonać następujące czynności:

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) 

jako contents nadal jest obiektem typu Python ctypes Structure, który jest zarządzany jako sam odnośnik.

Oczywistym obejściem byłoby ręczne skopiowanie każdego pola (reprezentowanego przez niezmienny python int), ale nie skaluje się przy bardziej złożonych strukturach. Ponadto należy wykonać rekursywnie dla pól, które nie są typami podstawowymi, ale strukturalnymi.

Moją drugą opcją jest użycie memmove i skopiowanie obiektów tak, jakby były buforami, ale wydaje się to bardzo podatne na błędy (ponieważ Python jest wpisywany dynamicznie, byłoby zbyt łatwo używać go z obiektami o różnych typach i rozmiarach, prowadząc do uszkodzenia pamięci lub błędów segmentacji) ...

Jakieś sugestie?

Edit:

mogę też użyć świeżego nową kopię struktury, więc może ta może być przydatna:

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

ale nie wiem, czy może istnieć jakiś rodzaj od zachowania dziwaczne kopiowanie ctypes Proxys jakby były zwykłymi obiektów Pythona ...

+1

Niestety 'deepcopy' zawiedzie, jeśli struktura ctypes zawiera wskaźniki:' ValueError: ctypes obiekty zawierające wskaźniki nie mogą być wytrawione. – 101

Odpowiedz

5

Można użyć przypisanie sekwencji skopiować wskazywanego do obiektów (zamiast przypisywania do p.contents, który zmienia wartość wskaźnika):

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 zrobi dla ciebie typ kontroli (co nie jest głupi -optymalne, ale lepsze niż nic).

Przykład użytku, sprawdzenia, czy ma to miejsce w rzeczywistości pracy;)

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

Wydaje się obiecujące, ale to po prostu się zmienia pointee: -s print addressof (src) i addressof (dst.contents) po przypisaniu do sprawdzenia – fortran

+0

Te funkcje nie powinny być przekazywane do wskaźników, one powinny być obiektami struktury 'ctypes'. chcę funkcji analogicznej do twojego C 'copy_point', do' dst [0] = src [0] '. – Miles

+0

Hmmmm ... Nie widzę powodu, dla którego zachowanie zmieniło się z robienia' dst = pointer (a); dst [0] = src; 'to 'pointer (a) [0] = src': - | – fortran

0

ja teraz też myśleć o zdefiniowanie metody jak:

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

Ale może być jeszcze ładniejsze opcji tam ...

+0

Zaleca się kilka błędów pisowni, ale należy sprawdzić poprawność. – whatnick

0

operacje Pointer jak reguły nie są bezpieczne bardzo pamięć. Chciałbym utworzyć klasy opakowujące dla każdego typu danych struct, które Cię interesują i pozwolić im obsługiwać operacje kopiowania wskaźników. Dokładnie tak, jak tu robisz. Istnieją funkcje lambda i map, które można używać rekursywnie jako cukier syntaktyczny.

+2

co za nieważne odpowiedź :-( – fortran

+0

Cena myślenia na głos .. są metaclasses, które mogą być używane miły mechanizm mapowania.http: //code.activestate.com/recipes/576666/ – whatnick

6

memmove jest tutaj prawidłowe działanie. Ustawiając argtypes funkcji CopyPoint, możesz łatwo wymusić bezpieczeństwo typu.

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 

wyjścia:

$ 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 

Zauważ, że można łatwo zrobić fabrykę do wygenerowania tego rodzaju funkcji w czasie wykonywania na podstawie określonego typu, jeśli trzeba uogólnić:

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 

wyjściowa:

<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

W python 3x twój kod może działać poprawnie. pokazany poniżej:

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