CPython的PyLong_FromLong(0L)永远不会失败?



这段代码来自Python C-API参考:

item = PyLong_FromLong(0L);
if (item == NULL)
goto error;

假设解释器是CPython,那么Python0对象的内存已经分配好了,那么会出现什么问题呢?读取PyLong_FromLong的源代码,对于小整数值,它将立即返回get_small_int((sdigit)0L)get_small_int的功能非常简单:

static PyObject *
get_small_int(sdigit ival)
{
assert(IS_SMALL_INT(ival));
PyThreadState *tstate = _PyThreadState_GET();
PyObject *v = (PyObject*)tstate->interp->small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
return v;
}

第一行的断言不会失败,因为PyLong_FromLong已经验证了它。根据其定义is unsafe: it does not check for error and it can return NULL.旁边的注释,_PyThreadState_GET()是一个宏。这看起来可能是失败的原因,但请注意,tstate->interp是正常访问的,如果宏返回NULL,则会中断解释器。之后,对Python0对象的所需引用是Py_INCREF'd,并返回给PyLong_FromLong(0L)的原始调用方。

我是否正确地理解了CPython的PyLong_FromLong对于小整数参数不会失败,或者我遗漏了什么?此外,为了完整性,用C编写的扩展模块可以从其他解释器中使用吗?或者在编写它们时,我可以假设它们将处理CPython?

是的,你是绝对正确的,这里永远不会有任何错误,小int是预先分配的,所以PyLong_FromLong(0L);永远不会失败。

但为什么还要测试是否为NULL?正如我所看到的,这只是遵循了第十部分:

  • 新建一个PyLongObject
  • 然后测试是否成功

PyLong_FromLong通过缓存小int来进行一些视觉化,但它仍然在大部分逻辑上返回一个新对象,尽管0L没有。作为函数调用程序,我们不应该依赖于内部缓存结构。因此,留下一些代码进行检查是有意义的,也是更安全的。

最新更新