【问题标题】:What was/is that "void* <unused>" argument for, that some __sizeof__ methods have in CPython?一些 __sizeof__ 方法在 CPython 中的“void* <unused>”参数是什么/是什么?
【发布时间】:2017-10-01 16:19:32
【问题描述】:

CPython 源代码中的几个 C 类型具有 __sizeof__ 方法,因此它们可以为具有 sys.getsizeof 的实例提供近似准确的大小(以字节为单位)。

这些方法被声明为METH_NOARG,但有些确实有void* whatever 参数,例如itertools.product.__sizeof__

static PyObject *
product_sizeof(productobject *lz, void *unused)
{
    Py_ssize_t res;

    res = _PyObject_SIZE(Py_TYPE(lz));
    res += PyTuple_GET_SIZE(lz->pools) * sizeof(Py_ssize_t);
    return PyLong_FromSsize_t(res);
}

static PyMethodDef product_methods[] = {
    /* ... */
    {"__sizeof__",      (PyCFunction)product_sizeof,      METH_NOARGS,  sizeof_doc},
    {NULL,              NULL}   /* sentinel */
};

有些拥有它(例如12),而有些则没有(例如:12)。当你声明一个没有参数的方法时,有一个参数似乎没有意义。

鉴于“未使用”这个名字,它似乎曾经有过某种意义,但我不知道是什么意思。我尝试使用“git blame”并阅读了一些相关问题,但找不到关于这个“未使用”论点的任何内容。我还认为它可能与 sys.getsizeof 的“默认”参数有关,但这并没有传递给方法 - 方法知道给出的默认值有什么意义......

我对此很感兴趣:争论的目的是什么(以及当它变得过时时为什么不删除它)。

【问题讨论】:

标签: python arguments cpython python-internals


【解决方案1】:

没有特定类型的函数只接受一个参数。 PyCFunctions 总是以两个作为其文档状态:

用于在 C 中实现大多数 Python 可调用函数的函数类型。这种类型的函数接受两个 PyObject* 参数并返回一个这样的值。

METH_NOARGS 的情况并不意味着该函数将只有一个参数,而是意味着第二个参数将始终为NULL

第一个参数通常命名为 self 并且将保存对模块或对象实例的引用。在所有情况下,第二个参数都是NULL

您也可以直接在拨打电话的call.c:_PyMethodDef_RawFastCallKeywords 中看到:

case METH_NOARGS:
    // After snipping checks away
    result = (*meth) (self, NULL);

对此有很多讨论,请参阅herehere 其中一些是here


至于只有一个参数的版本,正如 Martijn 指出的那样,这些使用参数诊所来隐藏它。

【讨论】:

  • 其实有一种方法只需要self:PyNoArgsFunction。但这似乎是一些残余(bugs.python.org/issue18091)......它没有在 CPython 源代码中的任何地方使用,甚至没有记录。
  • @MSeifert 确实,请参阅here 了解为什么它似乎仍然存在。 (P.s 奇怪的是我们的答案怎么这么匹配:-)
  • 糟糕的是它不是“可用的”。这对METH_NOARGs 来说非常方便。是的,几乎同时发布:)。
【解决方案2】:

基于 Martijn Pieters 的 cmets,我能够找出为什么这些方法有第二个参数。没有它列出的那些在参数诊所文件中有一个包装函数(所以它只是“隐藏”)。它不仅与__sizeof__ 相关,还与所有METH_NOARG 方法相关。

METH_NOARG documentation 是:

METH_NOARGS

没有参数的方法不需要检查是否有参数,如果它们是用METH_NOARGS 标志列出的。 它们的类型必须是 PyCFunction。第一个参数通常命名为 self 并且将保存对模块或对象实例的引用。在所有情况下,第二个参数都将为 NULL。

但是PyCFunctions 有两个参数:

PyCFunction

用于在 C 中实现大多数 Python 可调用函数的函数类型。这种类型的函数采用两个 PyObject* 参数并返回一个这样的值。如果返回值为 NULL,则应设置异常。如果不是 NULL,则返回值被解释为 Python 中公开的函数的返回值。该函数必须返回一个新的引用。

(强调我的)

对应的typedef可以在methodobject找到:

typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);

所以在PyMethodDef 中声明的所有方法都需要(至少)两个参数,即使它们不使用第二个参数。这就是为什么它被称为“未使用”,它对方法本身没有任何意义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    • 2019-03-11
    • 2017-04-29
    • 1970-01-01
    • 2016-10-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多