بيثون CTYPES: نسخ محتويات هيكل
سؤال
أريد أن تقليد قطعة من كود C في بيثون مع ctypes، الرمز هو شيء مثل:
typedef struct {
int x;
int y;
} point;
void copy_point(point *a, point *b) {
*a = *b;
}
في CTYPES، من غير الممكن القيام بما يلي:
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)
كما contents
لا يزال هو كائن بنية Python Ctypes، والتي تتم إدارتها كمرجع نفسه.
سيكون هناك حقل واضح يدويا نسخ كل حقل يدويا (يمثله بيثون كثافة ثابتة)، ولكن هذا لا يتجادل مع هياكل أكثر تعقيدا. أيضا، يجب القيام بذلك بشكل متكرر في الحقول التي ليست أساسية، ولكنها هيكلية.
خياري الآخر هو استخدام memmove
ونسخ الكائنات كما لو كانت مخزونا، لكن هذا يبدو عرضا للخطأ للغاية (كما يتم كتابة الثعبان ديناميكيا، سيكون من السهل جدا استخدامه مع كائنات من النوع والحجم المميز، مما يؤدي إلى تفويض الذاكرة أو أخطاء تجزئة) ...
أي اقتراحات؟
تعديل:
يمكنني أيضا استخدام نسخة جديدة جديدة من الهيكل، لذلك ربما هذا قد يكون مفيدا:
import copy
p0 = Point()
p1 = copy.deepcopy(p0) #or just a shallow copy for this example
لكنني لا أعرف ما إذا كان قد يكون هناك نوع من السلوكيات الغريبة التي نسخ cypes proxys كما لو كانت كائنات بيثون منتظمة ...
المحلول
يمكنك استخدام مهمة التسلسل لنسخ الكائنات المدببة (بدلا من تعيين p.contents
, أي تغيير قيمة المؤشر):
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
سوف تفعل نوع التحقق من أجلك (الذي لا يخدع مقاومة، ولكنه أفضل من لا شيء).
مثال على الاستخدام، مع التحقق الذي يفعله في الواقع؛):
>>> 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)
نصائح أخرى
memmove
هي العملية الصحيحة هنا. عن طريق تحديد argtypes
من وظيفة Copypoint الخاصة بك، يمكنك تطبيق سلامة النوع بسهولة.
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
المخرجات:
$ 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
لاحظ أنه يمكنك بسهولة إنشاء مصنع لتوليد هذا النوع من الوظيفة في وقت التشغيل بناء على نوع معين، إذا كنت بحاجة إلى تعميم:
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
انتاج:
<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>
عمليات المؤشر كقاعدة ليست آمنة لذاكرة للغاية. أود أن أنشئ فصول المجمع لكل نوع بيانات الهيكل الذي تهتم به واتركه التعامل مع عمليات نسخ المؤشر. إلى حد كبير كما تفعل هنا. هناك وظائف Lambda والخريطة التي يمكنك استخدامها بشكل متكرر مثل السكر النحوي.
أنا الآن أفكر أيضا في تحديد طريقة مثل:
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))
ولكن قد لا يزال هناك خيارات أجمل هناك ...
في بيثون 3X، يمكن تشغيل الكود الخاص بك بشكل صحيح. ظاهر أدناه:
>>> 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