3

我有一些 ctypes 绑定,对于每个 body.New 我应该调用 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/

4

3 回答 3

3

不幸的是,您想要做的是创建一个分配事物然后在不再使用该对象时自动解除分配的对象,这在 Python 中几乎是不可能的。不能保证调用del语句,因此您不能依赖它。

Python中的标准方法很简单:

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

或者从 2.5 开始,您还可以创建上下文管理器并使用 with 语句,这是一种更简洁的方式。

但这两者主要用于在代码片段的开头分配/锁定时。如果您想为程序的整个运行分配资源,则需要在启动时,在程序的主代码运行之前分配资源,然后再释放。这里没有涉及一种情况,那就是当您想要动态分配和取消分配许多资源并在代码中的许多地方使用它们时。例如,您想要一个内存缓冲区或类似的池。但是大多数情况都是针对内存的,Python 会为你处理,所以你不必担心这些。当然,在某些情况下,您希望对非内存的事物进行动态池分配,然后您会想要在示例中尝试的释放类型,那就是用 Python 做起来很棘手。

于 2009-06-25T14:33:21.093 回答
0

在 CPython 中,__del__ 它是对象的可靠析构函数,因为它总是会在引用计数达到零时被调用(注意:可能有一些情况——比如__del__定义了方法的项目的循环引用——引用计数永远不会达到零,但是那是另一个问题)。

更新 从评论中,我了解到问题与对象的销毁顺序有关:body是一个全局对象,它在所有其他对象之前被销毁,因此它们不再可用。
实际上,使用全局对象并不好;不仅因为这样的问题,还因为维护。

然后我会用这样的东西改变你的班级

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. 更改只是添加了对全局body对象的引用,因此它将至少与从该类派生的所有对象一样多。
  2. 尽管如此,由于单元测试和维护,使用全局对象并不好。最好有一个对象工厂,它将为类设置正确的“主体”,并且在单元测试的情况下很容易放置一个模拟对象。但这真的取决于你,以及你认为在这个项目中付出多少努力才有意义。
于 2009-06-25T16:55:14.357 回答
0

如果weakrefs没有被破坏,我想这可能会起作用:

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 没有释放我应该通过绑定释放的内存,这应该可以工作。是的,它基本上是一个 hack,但我认为它可能比我之前尝试的方法更好。

编辑:试过了,在对我的代码进行小的微调后似乎可以工作。令人惊讶的是,即使我从所有结构中删除了del,它似乎仍然失败。有趣但令人沮丧。

两者都不起作用,由于一些奇怪的机会,我已经能够在某些地方丢弃循环引用,但事情仍然没有解决。

编辑:嗯.. 弱引用毕竟被破坏了!所以在python中可能没有可靠的清理解决方案,除了强制它是明确的。

于 2009-06-25T14:52:06.547 回答