Py_INCREF/DECREF:何时使用

46 投票
1 回答
20440 浏览
提问于 2025-04-16 09:46

下面的说法是否正确:

  1. 如果在一个C语言的函数中创建了一个Python对象,但这个函数没有返回这个对象,那么就不需要增加引用计数(INCREF),但需要减少引用计数(DECREF)。

  2. [错误]如果这个函数返回了这个对象,那么在接收返回值的函数中需要增加引用计数(INCREF)。[/错误]

  3. 当把C语言类型的变量,比如doubleint等,作为属性赋值给Python对象时,不需要增加或减少引用计数(INCREFDECREF)。

  4. 下面的做法是否安全?将Python对象作为属性赋值给其他Python对象的方式是这样的:

    PyObject *foo;
    foo = bar;  // A Python object
    tmp = self->foo;
    Py_INCREF(foo);
    self->foo = foo;
    Py_XDECREF(tmp);
    // taken from the manual, but it is unclear if this works in every situation
    
  5. 当释放一个Python对象时,需要对它作为属性的每一个其他Python对象调用减少引用计数(DECREF),但对于C类型的属性则不需要。


编辑:

关于“C类型作为属性”,我指的是bar和baz:

typedef struct {
    PyObject_HEAD
    PyObject *foo;
    int bar;
    double baz;
} FooBarBaz;

1 个回答

54

首先,请仔细阅读一下这个内容,特别是最后一段,http://docs.python.org/extending/extending.html#ownership-rules

简单来说,可以把它想象成引用计数。

  1. 你说的第一点是对的。如果你创建了一个新的Python对象(比如说 PyLong),那么它的引用计数一开始就是1。如果你打算返回这个对象,那没问题;但如果你不打算返回,就需要让Python回收这个对象,而它只有在引用计数为0的时候才会被标记为可以回收,所以如果你不打算返回,就需要减少引用计数(DECREF)。

  2. 第二点是错的。如果你需要返回这个对象,并且是你自己创建的,那就直接返回它。返回的时候会转移所有权。如果你在返回之前增加引用计数(INCREF),那么你就是在告诉Python你自己也保留了一份副本。所以,如果你创建了这个对象,引用计数就是1。如果你再做一次INCREF,引用计数就变成2了。但这并不是你想要的,你希望返回时引用计数是1。

  3. 我不太确定我理解得对不对,但这更像是一个与C语言相关的问题。你是怎么把一个 intdouble 加到一个Python对象上的呢?

  4. 你能给个例子说明这种方法不管用的情况吗?

  5. 我还是不太明白C类型什么时候会成为Python对象的属性。每个 intdoublelong 等等,都是以某种方式被Python对象包装起来的。

这些回答的注意事项在上面的链接中有说明。其实在看完那个之后,你可能根本不需要我这个不太好的解释。我希望我能让你更清楚,而不是更困惑。

撰写回答