Py_DECREF和PY_INCREF的目的是什么?

15 投票
3 回答
12113 浏览
提问于 2025-04-18 11:17

我在看关于如何在Python中定义“新类型”的教程,https://docs.python.org/2/extending/newtypes.html,但是我不太明白在这段代码中使用Py_DECREF的目的是什么。

static PyObject *
Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    Noddy *self;

    self = (Noddy *)type->tp_alloc(type, 0);
    if (self != NULL) {
        self->first = PyString_FromString("");
        if (self->first == NULL)
          {
            Py_DECREF(self);
            return NULL;
          }

        self->last = PyString_FromString("");
        if (self->last == NULL)
          {
            Py_DECREF(self);
            return NULL;
          }

        self->number = 0;
    }

    return (PyObject *)self;
}

我对引用计数的理解还不太清楚,任何帮助都非常感谢。

3 个回答

1

引用计数是一种管理内存的方法,Python会记录有多少个“拥有引用”指向一个对象。当你创建一个新的“拥有引用”指向某个对象时,引用计数就需要加1;当一个拥有引用不再使用这个对象时,引用计数就需要减1。如果引用计数降到0,说明没有人再使用这个对象了,这时就可以释放它的内存。

C语言没有自动处理引用计数的功能,所以需要手动来管理。为了减少增加和减少引用计数的操作,Python的C API引入了“借用”和“窃取”引用的概念。一般来说,参数是借用引用,而返回值则是转移引用的所有权。不过也有一些例外,比如处理列表和元组的函数。

tp_alloc函数会创建一个新的对象,并给它一个引用,然后把这个引用的所有权转给你。在正常情况下,你会在完成对象的构建后,把这个引用的所有权再转给调用你函数的地方。

但如果出现问题,你可能会有一个构建不完全的对象需要处理,这时候就需要调用Py_Decref来减少引用计数。

11

在这个情况下,Py_DECREF 只是释放了用 tp->alloc 分配的内存。

tp->alloc 会把引用计数设置为 1。Py_DECREF 会把引用计数从 1 减少到 0;当它发现引用计数变为 0 时,就会调用相应的函数来释放内存(在这个例子中是 Noddy_dealloc)。

如果一个 Python C API 函数返回 NULL,那就说明出现了问题;通常会设置一个异常(保存在一个全局变量中)。

如果调用者再次返回 NULL,异常就会被链接在一起,所以才会出现 'return NULL' 的情况。

10

CPython的垃圾回收器使用了一种叫做“引用计数”的方法,也就是说,它会维护一个对象的引用列表。如果一个对象的引用计数降到零,就意味着这个对象可以被安全地释放掉,不再占用内存空间。

因此,当我们定义PyObjects时,必须明确地调用Py_INCREFPy_DECREF,这两个函数分别用来增加和减少对象的引用计数。

撰写回答