【问题标题】:I can't get __dealloc__ be called when deleting an object删除对象时无法调用 __dealloc__
【发布时间】:2013-04-05 04:04:29
【问题描述】:

我有以下 C++ 类:

.H

class ALabSet: public LabSet {
public:
    PyObject *m_obj;

    ALabSet(PyObject *obj);

    virtual ~ALabSet();

    PyObject *GetPyObj();

};

.CPP

ALabSet::ALabSet(PyObject *obj): LabSet() {

    this->m_obj = obj;
    // Provided by "cyelp_api.h"
    if (import_cyelp()) {
    } else {
        Py_XINCREF(this->m_obj);
    }

}


ALabSet::~ALabSet() {
    Py_XDECREF(this->m_obj);
}


PyObject *ALabSet::GetPyObj() {
    return this->m_obj;
}

我用 Cython 将其暴露如下:

cdef extern from "adapter/ALabSiteSetsManager.h" namespace "elps" :
    cdef cppclass ALabSet:
        ALabSet(PyObject *)

        PyObject *GetPyObj()



cdef class PyLabSet:
    cdef ALabSet *thisptr

    def __cinit__(self):
       self.thisptr = new ALabSet(<PyObject *>self)

    def __dealloc__(self):
       print "delete from PY !"
       if self.thisptr:
           del self.thisptr

我的问题是我不知道如何从 Python 中调用析构函数。以下内容完全没有任何作用:

a_set = PyLabSet()
del a_set

我在网上找不到类似的问题。有没有人想到这里出现在我身边?

我错过了一些关于引用计数管理的内容,或者...

非常感谢

【问题讨论】:

  • 您正在创建一个引用循环,我认为,在self.thisptr = new ALabSet(&lt;PyObject *&gt;self) 中,您正在增加类的引用计数。
  • 是的,魔法你!我删除了额外的 Py_XINCREF ,它成功了。好想法。谢谢。现在我不记得我在构造函数中做了什么。需要看一下显然由 Cython 生成的“import_cyelp()”,但我不知道它的作用了吗?
  • 我遇到了类似的问题,因为我正在创建循环引用。使用weakref.proxy 解决了这个问题,很好地描述了here

标签: c++ python destructor cython


【解决方案1】:

del a_set 删除对对象(局部变量)的引用。在 C++ 对象中还有另一个引用。这称为参考周期。循环 GC可以在一段时间后收集这个。但是,无法保证何时(甚至如果)会发生这种情况,因此您不应依赖它1

例如,包含带有 __del__ 特殊方法 are documented 的纯 Python 对象的引用循环不会被释放

在 3.4 版中更改:在 PEP 442 之后,具有 __del__() 方法的对象不再以 gc.garbage 结尾。

我不知道Cython's implementation of __dealloc__ 是否会触发此行为,但如前所述,破坏无论如何都不是确定性的。如果您想释放一些资源(例如,不是 Python 对象、文件、连接、锁等的内存块),您应该手动公开一种显式方式(参见close各种对象的方法)。上下文管理器可以简化执行此操作的客户端代码。

免责声明:几乎所有这些都是 CPython 特定的。

1 有些人更喜欢将 GC 视为一种模拟无限内存可用性的抽象,而不是破坏无法访问的对象的东西。使用这种方法,很明显破坏不是确定性的,甚至不能保证。

【讨论】:

  • 好的,感谢您提供有用的信息。我知道非确定性行为,但我有一个巨大的内存泄漏,因为我为每个对象创建了两倍的引用,正如@Wessie 建议的那样......
  • @Golgauth 实际上,在大多数情况下,对“打破”循环的引用保持沉默是错误的。这意味着引用不会使对象保持活动状态,因此它可能会消失在您的脚下,并且与弱引用(如果您足够关心,这是正确的解决方案)不同,您不会被告知何时发生并且无法检查有效性的参考。在您的特定情况下,它可能没问题,因为这两个对象的生命周期似乎完全相同。不过,对此要非常小心。保持引用循环(或使用弱引用)并提供明确的处理机制会更好。
  • 是的,你的权利。我现在可以看到了。当我直接使用从 python 返回的对象时(不将其存储在变量中 - 例如:Count(GetPyLabSet()),其中 GetPyLabSet() 返回一个 PyLabSet 对象),它已经被释放了。它给了一个很好的崩溃。看来我唯一的选择是保留我以前的 Py_XINCREF 并创建一个附加函数来显式调用 Py_XDECREF ...除非你们中的一个人对我的问题有第三个解决方案。再次感谢。
  • 我在保留对对象的引用时使用PyWeakref_NewRef,最后调用了dealloc :) 像这样this-&gt;m_obj = PyWeakref_NewRef(obj, NULL);我仍然使用Py_XINCREF
  • 还有一件事,因为扩展类必须在类中有cdef object __weakref__,这样我们才能创建一个弱引用。
猜你喜欢
  • 2015-11-23
  • 2021-12-29
  • 2012-07-25
  • 1970-01-01
  • 1970-01-01
  • 2019-04-12
  • 1970-01-01
  • 2011-06-23
  • 2012-11-25
相关资源
最近更新 更多