Search code examples
pythonargumentscpythonpython-internals

What was/is that "void* <unused>" argument for, that some __sizeof__ methods have in CPython?


Several C types in the CPython source have a __sizeof__ method so they can present approximately accurate sizes (in bytes) for instances with sys.getsizeof.

These methods are declared METH_NOARG but some do have a void* whatever argument, for example 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 */
};

Some have it (e.g. 1, 2), while others don't (for example: 1, 2). It doesn't seem to make sense to have an argument when you declare it a method without arguments.

Given the name "unused" it seems like it probably had some meaning once but I can't figure out for what. I've tried using "git blame" and reading through some of the related issues but couldn't find anything with respect to this "unused" argument. I also thought it may be related to the "default" argument for sys.getsizeof but that's not passed through to the method - and what would be the point for the method to know what default was given...

I'm interested it: What was the purpose of the argument (and when it became obsolete why wasn't it removed).


Solution

  • There's no specific type of function that only takes a single parameter. PyCFunctions always take two as its documentation states:

    Type of the functions used to implement most Python callables in C. Functions of this type take two PyObject* parameters and return one such value.

    The METH_NOARGS case doesn't mean that the function will only have a single parameter, rather, it means that the second parameter will always be NULL:

    The first parameter is typically named self and will hold a reference to the module or object instance. In all cases the second parameter will be NULL.

    you can also see this directly in call.c:_PyMethodDef_RawFastCallKeywords where the call is made:

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

    There's a number of discussions covering this, see here, here and here for some of these.


    As for the versions that only have a single argument, as Martijn points out, these use argument clinic to hide that.