在Python源码及扩展模块中,对引用计数的使用有三种方式:拥有引用、借用引用和窃取引用。

 

拥有引用:拥有引用比较简单,针对PyObject对象调用Py_INCREF或Py_XINCREF即可,如将一个对象加入到字典中时,会分别对key和value调用Py_INCREF

对于创建类函数,对象创建成功后,在函数的内部会自动增加引用计数,此类函数有:

PyObject* PyLong_FromLong(long v)

PyObject* PyLong_FromDouble

PyObject* PyInt_FromLong(long ival)

PyObject* PyString_FromString(const char *v)

...

 

 

借用引用:当Python对象做为函数参数传递时,如果函数内部仅仅是对参数的使用,并不需要内部保留对对象的引用,此时在函数内部不需要对参数增加引用计数,函数内部对参数对象的使用是借用调用者对参数拥有的所有权,其借用调用者的所有权,故称为借用引用。

 

窃取引用:这个概念相对难以理解,窃取引用最常见的情况是函数PyList_SetItem() 和 PyTuple_SetItem(),为了更好说明,现将函数 PyTuple_SetItem()的源码贴出,在向元组添加对象时,并未增加新加入对象(newitem)的引用计数,但在清空或添加出现异常时,减少新添加对象的引用计数,就好像其已经拥有(调用过Py_INCREF)了该对象一样。这就像是PyTuple_SetItem函数从调用者那里窃取了对象的所有权一样,故此种现象称为“窃取引用”。

 

int
PyTuple_SetItem(register PyObject *op, register Py_ssize_t i, PyObject *newitem)
{
    register PyObject *olditem;
    register PyObject **p;
    if (!PyTuple_Check(op) || op->ob_refcnt != 1) {
        Py_XDECREF(newitem);
        PyErr_BadInternalCall();
        return -1;
    }
    if (i < 0 || i >= Py_SIZE(op)) {
        Py_XDECREF(newitem);
        PyErr_SetString(PyExc_IndexError,
                        "tuple assignment index out of range");
        return -1;
    }
    p = ((PyTupleObject *)op) -> ob_item + i;
    olditem = *p;
    *p = newitem;
    Py_XDECREF(olditem);
    return 0;
}

 

为何会有如此怪异的设计,Python用户手册说的很明白,如下所示:

 

Few functions steal references; the two notable exceptions are PyList_SetItem() and PyTuple_SetItem(),

which steal a reference to the item (but not to the tuple or list into which the item is put!).

These functions were designed to steal a reference because of a common idiom for populating a tuple or list with newly created objects;

for example, the code to create the tuple (1, 2, "three") could look like this (forgetting about error handling for the moment;

 a better way to code this is shown below):

 

PyObject *t;

 

t = PyTuple_New(3);

PyTuple_SetItem(t, 0, PyInt_FromLong(1L));

PyTuple_SetItem(t, 1, PyInt_FromLong(2L));

PyTuple_SetItem(t, 2, PyString_FromString("three"));

 

原来,大多数情况下,这种写法最为简洁。