Python引用计数的三种方式

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

 

拥有引用:拥有引用比较简单,针对PyObject对象调用Py_INCREFPy_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"));

 

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

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值