I have an x, y parameters. Using the python C API, how can I default y to the value of x if y is None.
The python equivalent is:
def scale(x: float, y:Optional[float]=None):
if y is None:
y = x
c_scale_api(x, y)
I have an x, y parameters. Using the python C API, how can I default y to the value of x if y is None.
The python equivalent is:
def scale(x: float, y:Optional[float]=None):
if y is None:
y = x
c_scale_api(x, y)
just check it against Py_None.
static PyObject* scale(PyObject* self, PyObject* args) {
PyObject* input2 = Py_None; // initialize to None
double input1d, input2d;
if (!PyArg_ParseTuple(args, "d|O", &input1d, &input2)) {
return NULL;
}
if (input2 == Py_None)
{
input2d = input1d;
}
else
{
input2d = PyFloat_AsDouble(input2);
}
return PyFloat_FromDouble(input2d);
}
this will return the value of the second argument for you to check.
You can use | to state that some arguments are optional when parsing the arg tuple. You must ensure that any arguments after the | always have a value before you use them. That is, PyArg_ParseTuple will leave the variables untouched if the argument was not present. Usually, that means you should assign the default values before you call PyArg_ParseTuple. However, because of your requirement, we can just check how many parameters we actually got, and then assign to y if only 1 argument was given.
This code will make the second argument optional, but is not fully equivalent to the python function. That is None is not a valid value for the second argument. So scale(1, 2) and scale(1) are both fine, but scale(1, None) would fail.
static PyObject*
scale(PyObject* self, PyObject* args) // type: METH_VARARGS
{
double x;
double y;
if (!PyArg_ParseTuple(args, "d|d", &x, &y)) {
return NULL;
}
if (PyTuple_GET_SIZE(args) == 1) {
y = x;
}
return PyFloat_FromDouble(x + y);
}
For a fully equivalent C function try the following. Now things like scale(x=1, y=None) will work as well.
static PyObject*
scale(PyObject* self, PyObject* args, PyObject* kwargs) // type: METH_VARARGS | METH_KEYWORDS
{
static char* keywords[] = { "x", "y", NULL };
double x;
double y;
PyObject* y_obj;
if (
!PyArg_ParseTupleAndKeywords(
args, kwargs, "d|O:scale", keywords, &x, &y_obj
)
) {
return NULL;
}
if (y_obj == Py_None) {
y = x;
} else if (PyFloat_Check(y_obj)) {
y = PyFloat_AS_DOUBLE(y_obj);
} else if (PyLong_Check(y_obj)) {
y = PyLong_AS_LONG(y_obj);
} else {
PyErr_SetString(PyExc_TypeError, "y must be float | int | None");
return NULL;
}
return PyFloat_FromDouble(x + y);
}