سؤال

أريد أن تقليد قطعة من كود 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top