سؤال

لدي بعض روابط ctypes، ولكل body.New يجب أن أسميه body.Free.لا تحتوي المكتبة التي أقوم بربطها على إجراءات تخصيص معزولة عن بقية الكود (يمكن الاتصال بها في أي مكان هناك)، ولاستخدام بعض الميزات المفيدة أحتاج إلى عمل مراجع دورية.

أعتقد أن الأمر سيحل إذا وجدت طريقة موثوقة لربط المدمر بجسم ما.(ستساعد المراجع الضعيفة إذا أعطوني رد الاتصال فقط قبل يتم إسقاط البيانات.

من الواضح أن هذا الرمز يفشل بشكل كبير عندما أضع velocity_func:

class Body(object):
    def __init__(self, mass, inertia):
        self._body = body.New(mass, inertia)

    def __del__(self):
        print '__del__ %r' % self
        if body:
            body.Free(self._body)

    ...        

    def set_velocity_func(self, func):
        self._body.contents.velocity_func = ctypes_wrapping(func)

لقد حاولت أيضًا حل المشكلة من خلال المراجع الضعيفة، حيث تبدو الأمور وكأنها تزداد سوءًا، ولكن فقط لا يمكن التنبؤ بها إلى حد كبير.

حتى لو لم أضع velocity_func، فستظهر دورات على الأقل عندما أفعل هذا:

class Toy(object):
    def __init__(self, body):
        self.body.owner = self

...

def collision(a, b, contacts):
    whatever(a.body.owner)

فكيف نتأكد من أن الهياكل سوف يتم جمع القمامة، حتى لو تم تخصيصها/تحريرها بواسطة المكتبة المشتركة؟

يوجد مستودع إذا كنت مهتمًا بمزيد من التفاصيل: http://bitbucket.org/cheery/ctypes-chipmunk/

هل كانت مفيدة؟

المحلول 3

إذا لم يتم كسر الضعف، أعتقد أن هذا قد ينجح:

from weakref import ref

pointers = set()

class Pointer(object):
    def __init__(self, cfun, ptr):
        pointers.add(self)
        self.ref = ref(ptr, self.cleanup)
        self.data = cast(ptr, c_void_p).value # python cast it so smart, but it can't be smarter than this.
        self.cfun = cfun

    def cleanup(self, obj):
        print 'cleanup 0x%x' % self.data
        self.cfun(self.data)
        pointers.remove(self)

def cleanup(cfun, ptr):
    Pointer(cfun, ptr)

ما زلت أحاول ذلك.القطعة المهمة هي أن المؤشر لا يحتوي على أي إشارات قوية للمؤشر الخارجي، باستثناء عدد صحيح.يجب أن يعمل هذا إذا لم تحرر ctypes الذاكرة التي يجب أن أقوم بتحريرها باستخدام الارتباطات.نعم، إنه في الأساس اختراق، لكنني أعتقد أنه قد يعمل بشكل أفضل من الأشياء السابقة التي كنت أحاولها.

يحرر:لقد جربته، ويبدو أنه يعمل بعد ضبط بسيط للكود الخاص بي.الشيء المدهش هو أنه حتى لو حصلت ديل من كل بنياتي، يبدو أنه لا يزال يفشل.مثيرة للاهتمام ولكنها محبطة.

لم ينجح أي منهما، من خلال فرصة غريبة تمكنت من إسقاط المراجع الدورية في بعض الأماكن، لكن الأمور ظلت معطلة.

يحرر:حسنًا..لقد تم كسر الضعفاء بعد كل شيء!لذلك من المحتمل ألا يكون هناك حل للتنظيف الموثوق به في بايثون، باستثناء إجبارها على أن تكون صريحة.

نصائح أخرى

ما تريد القيام به هو إنشاء كائن يخصص الأشياء ثم يلغي التخصيص تلقائيًا عندما لا يكون الكائن قيد الاستخدام، وهو أمر مستحيل تقريبًا في بايثون، لسوء الحظ.ال ديل لا يمكن ضمان استدعاء البيان، لذا لا يمكنك الاعتماد على ذلك.

الطريقة القياسية في بايثون هي ببساطة:

try:
    allocate()
    dostuff()
finally:
    cleanup()

أو منذ الإصدار 2.5، يمكنك أيضًا إنشاء مديري السياق واستخدام العبارة with، وهي طريقة أفضل للقيام بذلك.

لكن كلاهما مخصصان بشكل أساسي عند تخصيص/قفل بداية مقتطف التعليمات البرمجية.إذا كنت تريد تخصيص الأشياء لتشغيل البرنامج بالكامل، فأنت بحاجة إلى تخصيص المورد عند بدء التشغيل، قبل تشغيل الكود الرئيسي للبرنامج، وإلغاء التخصيص بعد ذلك.هناك موقف واحد لم يتم تناوله هنا، وهو عندما تريد تخصيص العديد من الموارد وإلغاء تخصيصها ديناميكيًا واستخدامها في العديد من الأماكن في التعليمات البرمجية.على سبيل المثال، تريد مجموعة من مخازن الذاكرة المؤقتة أو ما شابه ذلك.لكن معظم هذه الحالات مخصصة للذاكرة، والتي ستتعامل معها بايثون نيابةً عنك، لذلك لا داعي للقلق بشأنها.هناك بالطبع حالات تريد فيها تخصيص تجمع ديناميكي لأشياء ليست من الذاكرة، ومن ثم قد ترغب في نوع إلغاء التخصيص الذي تجربه في المثال الخاص بك، وذلك يكون من الصعب القيام به مع بايثون.

في سي بايثون، __del__ يكون أداة تدمير موثوقة للكائن، لأنه سيتم استدعاؤها دائمًا عندما يصل العدد المرجعي إلى الصفر (ملاحظة:قد تكون هناك حالات - مثل المراجع الدائرية للعناصر ذات __del__ الطريقة المحددة - حيث لن يصل العدد المرجعي إلى الصفر أبدًا، ولكن هذه مشكلة أخرى).

تحديثأفهم من التعليقات أن المشكلة تتعلق بترتيب تدمير الأشياء: جسم هو كائن عالمي، ويتم تدميره قبل كل الكائنات الأخرى، وبالتالي لم يعد متاحًا لهم.
في الواقع، استخدام الكائنات العالمية ليس جيدًا؛ليس فقط بسبب مشكلات مثل هذه، ولكن أيضًا بسبب الصيانة.

سأقوم بعد ذلك بتغيير فصلك بشيء كهذا

class Body(object):
    def __init__(self, mass, inertia):
        self._bodyref = body
        self._body = body.New(mass, inertia)

    def __del__(self):
        print '__del__ %r' % self
        if body:
            body.Free(self._body)

...        

def set_velocity_func(self, func):
    self._body.contents.velocity_func = ctypes_wrapping(func)

بضع ملاحظات:

  1. التغيير يضيف فقط مرجعًا إلى global جسم الكائن، وبالتالي سيعيش على الأقل بقدر كل الكائنات المشتقة من تلك الفئة.
  2. ومع ذلك، فإن استخدام كائن عمومي ليس جيدًا بسبب اختبار الوحدة وصيانتها؛من الأفضل أن يكون لديك مصنع للكائن، والذي سيحدد "الجسم" الصحيح للفئة، وفي حالة اختبار الوحدة سيضع كائنًا وهميًا بسهولة.ولكن الأمر متروك لك حقًا ومدى الجهد الذي تعتقد أنه منطقي في هذا المشروع.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top