Define a variable of type PyNumberMethods
and include
a pointer to it in the tp_as_number
slot in the
PyTypeObject
definition of a new type.
Each callback which returns a pointer to a PyObject should return a new object of the same type. But this is not required.PyNumberMethods counter_as_number = { counter_add, /* binaryfunc nb_add; /* __add__ */ counter_sub, /* binaryfunc nb_subtract; /* __sub__ */ counter_mul, /* binaryfunc nb_multiply; /* __mul__ */ counter_div, /* binaryfunc nb_divide; /* __div__ */ counter_mod, /* binaryfunc nb_remainder; /* __mod__ */ counter_divmod, /* binaryfunc nb_divmod; /* __divmod__ */ counter_pow, /* ternaryfunc nb_power; /* __pow__ */ counter_neg, /* unaryfunc nb_negative; /* __neg__ */ counter_pos, /* unaryfunc nb_positive; /* __pos__ */ counter_abs, /* unaryfunc nb_absolute; /* __abs__ */ counter_nonzero, /* inquiry nb_nonzero; /* __nonzero__ */ counter_invert, /* unaryfunc nb_invert; /* __invert__ */ counter_lshift, /* binaryfunc nb_lshift; /* __lshift__ */ counter_rshift, /* binaryfunc nb_rshift; /* __rshift__ */ counter_and, /* binaryfunc nb_and; /* __and__ */ counter_xor, /* binaryfunc nb_xor; /* __xor__ */ counter_or, /* binaryfunc nb_or; /* __or__ */ counter_coerce, /* coercion nb_coerce; /* __coerce__ */ counter_int, /* unaryfunc nb_int; /* __int__ */ counter_long, /* unaryfunc nb_long; /* __long__ */ counter_float, /* unaryfunc nb_float; /* __float__ */ counter_oct, /* unaryfunc nb_oct; /* __oct__ */ counter_hex, /* unaryfunc nb_hex; /* __hex__ */ };
If the nb_coerce
slot contains a function, then coercion
is attempted before other methods are called.
Writing a good coercion routine can be difficult (in Python or C),
but will save rewriting the little "add" and "multiply"
functions.
If a slot does not contain a function, replace it with 0.
For conversion of arguments, it is sometimes useful to have a helper function or two. This is to convert the data to a consistent format; for example, to an integer or float.
int object2int(obj, value) PyObject *obj; long *value; { PyObject *tmp; if (Counter_check(obj)) *value = Counter_value(obj); else if (PyInt_Check(obj)) *value = PyInt_AsLong(obj); else { tmp = PyNumber_Int(obj); if (tmp == NULL) { PyErr_SetString(PyExc_TypeError, "expecting number"); return 0; } *value = PyInt_AsLong(tmp); Py_DECREF(tmp); } return 1; } /* return the integer values of the two objects given */ int objects2ints(obj1, obj2, value1, value2) PyObject *obj1, *obj2; long *value1, *value2; { if (Counter_Check(obj1) && Counter_Check(obj2)) { *value1 = Counter_Value(obj1); *value2 = Counter_Value(obj2); } else if (Counter_Check(obj1)) { *value1 = Counter_Value(obj1); if (!object2int(obj2, value2)) return 0; } else { *value2 = Counter_Value(obj2); if (!object2int(obj1, value1)) return 0; } return 1; }
All the arithmetic operations are the forward operations ("__add__" vs. "__radd__"). Coercion is the proper mechanism to handle the reverse operations. But writing the functions as if either argument is of the type (with the aid of helper functions), can simplify the task for you.
Some writers choose to make a divmod helper function which is called by the "nb_div", "nb_mod" and "nb_divmod" functions. If the division operation is complex, this can simplify the code (and make it easier to change later).PyObject * counter_add(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; return counter_NEW(vi + wi); } PyObject * counter_sub(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; return counter_NEW(vi - wi); } PyObject * counter_mul(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; return counter_NEW(vi * wi); } PyObject * counter_div(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; if (wi == 0) { PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo"); return (PyObject *)NULL; } return counter_NEW(vi / wi); } PyObject * counter_mod(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; if (wi == 0) { PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo"); return (PyObject *)NULL; } return counter_NEW(vi % wi); } PyObject * counter_divmod(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; if (wi == 0) { PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo"); return (PyObject *)NULL; } return Py_BuildValue("(ii)", (vi / wi), (vi % wi)); }
The "nb_nonzero" function is used for a truth value, it returns
an int
instead of a Python object.
It is sometimes unsure whether "__pos__" should return the object itself; a good rule is: when in doubt, create a new object.
The pow() function above is taken from the Python 1.5.2 code as an example.PyObject * counter_pow(v, w, z) PyObject *v, *w, *z; { long i, temp, iz, x, prev; iz = 0; if (!objects2ints(v, w, &i, &temp) || (z != Py_None && !object2int(z &iz))) return (PyObject *)NULL; x = 1; while (i > 0) { prev = x; if (i & 1) { x *= temp; if (temp == 0) break; if (x / temp != prev) { PyErr_SetString(PyExc_OverflowError, "counter pow()"); return (PyObject *)NULL; } } i >>= 1; if (i == 0) break; prev = temp; temp *= temp; if (prev != 0 && temp / prev != prev) { PyErr_SetString(PyExc_OverflowError, "counter pow()"); return (PyObject *)NULL; } if (iz) { x %= iz; temp %= iz; } } if (iz) { PyObject *t1, *t2, *tr; long int div, mod; t1 = PyInt_FromLong(x); t2 = PyInt_FromLong(iz); if (t1 == NULL || t2 == NULL || (tr = PyNumber_Divmod(t1, t2)) == NULL) { Py_XDECREF(t1); Py_XDECREF(t2); return (PyObject *)NULL; } Py_DECREF(t1); Py_DECREF(t2); if (!PyArg_Parse(tr, "(ii)", &div, &mod)) return (PyObject *)NULL; x = mod; } return counter_NEW(x); } PyObject * counter_neg(self) PyObject *self; { return counter_NEW(-Counter_value(self)); } PyObject * counter_pos(self) PyObject *self; { return counter_NEW(Counter_value(self)); } PyObject * counter_abs(self) PyObject *self; { register long value = Counter_value(self); return counter_NEW(value < 0 ? -value : value); } int counter_nonzero(self) PyObject *self; { return (Counter_value(self) != 0; }
PyObject * counter_invert(self) PyObject *self; { return counter_NEW(~Counter_value(self)); } PyObject * counter_lshift(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; return counter_NEW(vi << wi); } PyObject * counter_rshift(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; return counter_NEW(vi >> wi); } PyObject * counter_and(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; return counter_NEW(vi & wi); } PyObject * counter_xor(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; return counter_NEW(vi ^ wi); } PyObject * counter_or(v, w) PyObject *v, *w; { long vi, wi; if (!objects2ints(v, w, &vi, &wi)) return (PyObject *)NULL; return counter_NEW(vi | wi); }
The conversion routines are separated into three parts:
__float__
, __int__
and
__long__
.
__repr__
and __str__
.
__hex__
and __oct__
.
The __oct__
function should return a string that starts
with "0" and
the __hex__
function should return a string that starts
with "0x".
PyObject * counter_int(self) PyObject *self; { return PyInt_AsLong(Counter_value(self)); } PyObject * counter_long(self) PyObject *self; { return PyLong_AsLong(Counter_value(self)); } PyObject * counter_float(self) PyObject *self; { return PyFloat_AsDouble((double)Counter_value(self)); } PyObject * counter_oct(self) PyObject *self; { char buf[20]; sprintf(buf, "0%o", Counter_value(self)); return Py_BuildValue("s", buf); } PyObject * counter_hex(self) PyObject *self; { char buf[20]; sprintf(buf, "0x%x", Counter_value(self)); return Py_BuildValue("s", buf); }
In this example, everything but complex objects are converted to a counter object. If the other object is complex, then coerce the counter object to a complex number.
Copyright (C) 1999 Michael P. Reilly, All rights reserved.int counter_coerce(v, w) PyObject **v, **w; { long i; /* the case where both are the same type should be handled already */ if (PyComplex_Check(*w)) { *v = PyComplex_FromDoubles((double)Counter_value(*v), (double)0.0); Py_INCREF(*w); return 0; } else if (object2int(*w, &i)) *w = counter_New(i); Py_INCREF(*v); return 0; } return 1; /* couldn't do it */ }