Вопрос

У меня есть несколько привязок ctypes, и для каждого тела.Новое я должен назвать body.Free.Библиотека, которую я привязываю, не имеет процедур выделения, изолированных от остальной части кода (они могут быть вызваны где угодно), и чтобы использовать пару полезных функций, мне нужно создавать циклические ссылки.

Я думаю, это решило бы проблему, если бы я нашел надежный способ привязать деструктор к объекту.(weakrefs помогли бы, если бы они дали мне обратный вызов просто до того, как данные будут удалены.

Итак, очевидно, что этот код выходит из строя, когда я добавляю 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)

Я также пытался решить эту проблему с помощью weakrefs, но с ними ситуация, похоже, становится только хуже, только в значительной степени более непредсказуемой.

Даже если я не введу функцию 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 не освобождает память, которую я должен освободить с помощью привязок. Да, это в основном хак, но я думаю, что это может работать лучше, чем предыдущие вещи, которые я пробовал.

Редактировать: пробовал, и, кажется, он работает после небольшой настройки моего кода. Удивительно, что даже если я вытащил del из всех своих структур, он все равно потерпел неудачу. Интересно, но расстраивает.

Ни одна из них не работает, по какой-то странной случайности я смог отбросить циклические ссылки в некоторых местах, но все остаётся сломанным.

Редактировать: Ну ... слабые стороны БЫЛИ сломаны в конце концов! так что, скорее всего, нет никакого решения для надежной очистки в python, кроме как сделать его явным.

Другие советы

То, что вы хотите сделать, это создать объект, который распределяет вещи, а затем автоматически освобождает их, когда объект больше не используется, к сожалению, в Python практически невозможно. Оператор del не гарантированно вызывается, поэтому на него нельзя полагаться.

Стандартный способ в Python - это просто:

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

Или, начиная с версии 2.5, вы также можете создавать контекстные менеджеры и использовать оператор with, что является более точным способом сделать это.

Но оба они предназначены, прежде всего, для выделения / блокировки в начале фрагмента кода. Если вы хотите, чтобы вещи выделялись на весь прогон программы, вам нужно выделить ресурс при запуске, до запуска основного кода программы, а затем освободить. Есть одна ситуация, которая здесь не рассматривается, и это когда вы хотите динамически распределять и освобождать много ресурсов и использовать их во многих местах кода. Например, вы хотите пул буферов памяти или подобное. Но большинство из этих случаев касаются памяти, которую Python будет обрабатывать для вас, так что вам не придется беспокоиться об этом. Есть, конечно, случаи, когда вы хотите динамически распределять пул вещей, которые НЕ являются памятью, а затем вам нужен тип освобождения, который вы пытаетесь использовать в своем примере, и что является сложным для Python .

В CPython, __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. Изменение заключается только в добавлении ссылки на глобальный Тело объект, который, таким образом, будет жить по крайней мере столько же, сколько все объекты, производные от этого класса.
  2. Тем не менее, использование глобального объекта нехорошо из-за модульного тестирования и обслуживания;лучше было бы иметь фабрику для объекта, которая установит правильное "тело" для класса, и в случае модульного тестирования легко поместит макет объекта.Но на самом деле это зависит от вас и от того, сколько усилий, по вашему мнению, имеет смысл вложить в этот проект.
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top