【发布时间】:2020-06-03 01:36:16
【问题描述】:
这段代码来自Python C-API reference:
item = PyLong_FromLong(0L);
if (item == NULL)
goto error;
假设解释器是 CPython,那么 Python 0 对象的内存是 already allocated,那么那里会出现什么问题呢?阅读PyLong_FromLong 的源代码,对于小整数值,它将是immediately return get_small_int((sdigit)0L)。 get_small_int 函数真的是very simple:
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 已经验证了它。 _PyThreadState_GET() 是一个宏,根据其定义旁边的 comment,is unsafe: it does not check for error and it can return NULL. 这可能看起来像是故障源,但请注意tstate->interp 可以正常访问,这会出现段错误如果宏返回了NULL,则为解释器。之后,对 Python 0 对象的想要引用是 Py_INCREF'd 并返回给 PyLong_FromLong(0L) 的原始调用者。
我是否正确理解 CPython 的 PyLong_FromLong 对于小整数参数不会失败,还是我错过了什么?另外,为了完整起见,是否可以从其他解释器中使用用 C 编写的扩展模块,或者在编写它们时我可以假设它们将处理 CPython?
【问题讨论】:
-
哦,make this assumption fail with
ctypes可能是微不足道的。问题提到了 32 位平台,但是在运行在 64 位平台上的 Python 3 解释器上,您可以尝试类似ctypes.c_long.from_address(id(1) + 24).value = 2来操纵1的基础值,然后执行1 == 2并且解释器可能会出现段错误. -
很好,虽然我肯定会认为这不会发生,呵呵。
-
PyPy 尝试定义 C API 兼容层(我认为 Jython 也是如此),因此您总是在处理 CPython 的假设可能不正确。但无论如何可能仍然是一个合理的假设。我想我只是对基于被认为是“小整数”的代码编写代码有点谨慎
标签: python error-handling python-c-api