cpython - 为什么 PySequence_GetItem 返回一个新的引用?
问题描述
我被 C/C++ python 扩展中的内存泄漏所困扰,因为假设它PySequence_GetItem
会返回一个借用的引用,就像PyList_GetItem
and一样PyTuple_GetItem
。我的问题是:为什么PySequence_GetItem
返回一个新的引用而PyList_GetItem
返回PyTuple_GetItem
借来的引用?
从文档:
PyObject* PySequence_GetItem(PyObject *o, Py_ssize_t i)
Return value: New reference.
Return the ith element of o, or NULL on failure. This is the equivalent of the Python expression o[i].
解决方案
您从 获得新的引用PySequence
,因为这是PySequence
-protocol 定义的。
然而,以这种方式定义协议有充分的理由:并非所有序列都由内存支持(如list
, tuple
),因为某些项目是动态创建的(如range
, unicode
)。
因为list
和tuple
所有项目都归list
/拥有tuple
(它们不是临时对象)所以我们可以借用它们(借用是一个小的优化) - list
/tuple
最终会释放内存。
range
是序列的另一个例子。它实现了 PySequence-protocol):
static PySequenceMethods range_as_sequence = {
(lenfunc)range_length, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
(ssizeargfunc)range_item, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
(objobjproc)range_contains, /* sq_contains */
};
但是,返回的对象PySequence_GetItem
是临时的(即没有人在函数之外拥有对它的引用),我们可以在源代码中验证这一点range_item
:
static PyObject *
range_item(rangeobject *r, Py_ssize_t i)
{
PyObject *res, *arg = PyLong_FromSsize_t(i);
if (!arg) {
return NULL;
}
res = compute_range_item(r, arg);
Py_DECREF(arg);
return res;
}
归结compute_range_item
为compute_item
:
static PyObject *
compute_item(rangeobject *r, PyObject *i)
{
PyObject *incr, *result;
/* PyLong equivalent to:
* return r->start + (i * r->step)
*/
incr = PyNumber_Multiply(i, r->step);
if (!incr)
return NULL;
result = PyNumber_Add(r->start, incr);
Py_DECREF(incr);
return result;
}
没有人拥有返回的对象result
,因此接收者必须注意减少引用计数。
可能还有其他解决方案(某种对已创建项目的缓存),但返回新引用是处理动态创建项目问题的最简单/透明的方法。
推荐阅读
- html - How to customize each column's fluidity in css?
- django - 如何将 url 保存到 django 模型中
- apache - Apache-Tomcat Servlet-mapping through url-pattern fails (404-Error)
- ios - Fastlane Apple 开发者帐户凭据
- sql - 将日期转换为数字数据类型
- websphere - 在 Node-RED 中使用 Watson Assistant V2 的 TypeError
- linux - Ansible 没有在 hosts 文件中找到 localhost
- python - PySpark:收集带有嵌套列的数据框作为字典
- regex - HIVE 中的 Regexp_Replace 仅保留某些单词
- mysql - 在不同的表上获取电话号码区号和查找区域