How does a new style number indicate that it is new style ?
A new style number is considered by the interpreter as such if and only it the nb_coerce sub slot of the numeric slot is set to PY_NEWSTYLENUMBER. Old style numbers will either have set this slot to the address of a C function, or to NULL.
How are new style number slots to be implemented ?
The main difference between old style slots and new style ones is that the slot functions can no longer assume to be passed arguments of identical type. New style slots must check all arguments for proper type and implement the necessary conversions themselves. This may seem to cause more work on the behalf of the type implementor, but is in fact no more difficult than writing the same kind of routines for an old style coercion slot.
If a new style slot finds that it cannot handle the passed argument type combination, it may return a new reference of the special singleton Py_NotImplemented to the caller. This will cause the caller to try the other operands operation slots until it finds a slot that does implement the operation for the specific type combination. If none of the possible slots succeed, it raises a TypeError.
Here is a sample numeric slot (that of the standard integer object converted to new style):
static PyNumberMethods int_as_number = {
(binaryfunc)int_add, /*nb_add*/
(binaryfunc)int_sub, /*nb_subtract*/
(binaryfunc)int_mul, /*nb_multiply*/
(binaryfunc)int_div, /*nb_divide*/
(binaryfunc)int_mod, /*nb_remainder*/
(binaryfunc)int_divmod, /*nb_divmod*/
(ternaryfunc)int_pow, /*nb_power*/
(unaryfunc)int_neg, /*nb_negative*/
(unaryfunc)int_pos, /*nb_positive*/
(unaryfunc)int_abs, /*nb_absolute*/
(inquiry)int_nonzero, /*nb_nonzero*/
(unaryfunc)int_invert, /*nb_invert*/
(binaryfunc)int_lshift, /*nb_lshift*/
(binaryfunc)int_rshift, /*nb_rshift*/
(binaryfunc)int_and, /*nb_and*/
(binaryfunc)int_xor, /*nb_xor*/
(binaryfunc)int_or, /*nb_or*/
PY_NEWSTYLENUMBER, /*nb_coerce*/
(unaryfunc)int_int, /*nb_int*/
(unaryfunc)int_long, /*nb_long*/
(unaryfunc)int_float, /*nb_float*/
(unaryfunc)int_oct, /*nb_oct*/
(unaryfunc)int_hex, /*nb_hex*/
/* New style slots: */
(binaryfunc)int_cmp, /*nb_cmp*/
};
Only one new slot is currently necessary. The new slots are only accessed in case nb_coerce is set to PY_NEWSTYLENUMBER, thus retaining backward binary compatibility.
How does the interpreter implement numeric operations ?
To make the implementation easy to understand (the whole topic is esoteric enough), a new layer in the handling of numeric operations is introduced. This layer takes care of all the different cases that need to be taken into account when dealing with all the possible combinations of old and new style numbers. It is implemented by the two functions _PyNumber_BinaryOperation() and _PyNumber_TernaryOperation(), which both are internal functions that only the functions in Objects/abstract.c have access to. The numeric API (PyNumber_*) is easy to adapt to this new layer.
As a side-effect all numeric slots can be NULL-checked (this has to be done anyway, so the added feature comes at no extra cost).
The scheme used by the layer to execute a binary operation is as follows:
The indicated action sequence is executed from left to right until either the operation succeeds and a valid result (!= Py_NotImplemented) is returned or an exception is raised. Exceptions are returned to the calling function as-is. If a slot returns Py_NotImplemented, the next item in the sequence is executed.
Note that coerce(v,w) will use the old style nb_coerce slot methods via a call to PyNumber_Coerce().
Ternary operations have a few more cases to handle:
if z != Py_None:
coerce(v,w), coerce(v,z), coerce(w,z)
else:
# treat z as absent variable
coerce(v,w)
The current implementation uses this scheme already (there's only one ternary slot: nb_pow(a,b,c)).
Note that the numeric protocol is also used for some other related tasks, e.g. sequence concatenation. These can also benefit from the new mechanism by implementing right-hand operations for type combinations that would otherwise fail to work. As an example, take string concatenation: currently you can only do string + string. With the new mechanism, a new string-like type could implement new_type + string and string + new_type, even though strings don't know anything about new_type.
What other changes are necessary to implement this new style scheme ?
Since comparisons also rely on coercion (every time you compare an integer to a float, the integer is first converted to float and then compared...), a new slot to handle numeric comparisons is needed:
Numeric comparisons are handled by a new numeric protocol API:
Other changes include adapting some of the built-in functions (e.g. cmp()) to use this API as well. Also, PyNumber_CoerceEx() will need to check for new style numbers before calling the nb_coerce slot. New style numbers don't provide a coercion slot and thus cannot be explicitly coerced.
What does not work on new style numbers ?
Obviously, since the coercion slot is gone, explicitly coercing new style numbers is not supported.
What are the benefits of this proposal ?
All of them should be converted to the new style numbers, since it makes them faster (a tad) and more flexible.
OK, I like the proposal, now who's going to write the code ?
Most of the proposal is already implemented. All you have to do is download the patch and apply it (according to the included instructions) to a clean Python 1.5.1 source distribution. Please post any feedback either to me or the newsgroup.
The patch includes a special feature that helps you while debugging new style numbers: if you compile the source with compiler switch '-DCOERCION_DEBUG', debug code will be enabled. Starting Python with switch '-d', e.g. 'python -d myscript.py', will then print lots of information about what the new style coercion mechanism is doing.
Things that still need to be done:
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4