I'm also keen to understand this and think I can elucidate a little,
from my study of the code. The perspective of someone who *didn't*
write it might help, but I defer to the authors' version when it appears.
Thank you! I'd be happy to know the authors' opinion as well.
A crucial observation is that there is only one nb_add slot in a type
definition. Think about adding a int(1) + float(2). Where it lands in
long_add (v->ob_type->tp_as_number->nb_add) will return
NotImplemented, because it does not understand the float right-hand
argument, but float_add (w->ob_type->tp_as_number->nb_add) is able to
give an answer, since it can float the int, behaving as
float.__radd__(f, i). So the slots have to implement both __add__ and
__radd__.
I agree. And by default, this is done by expanding the macro
"SLOT1BIN(slot_nb_add, nb_add, "__add__", "__radd__")".
The logic is the same as binary_op1, but the function has to deal with
the possibility that one or other type may already provide a special
function wrapper function in its nb_add slot, which is what the test
tp_as_number->SLOTNAME == TESTFUNC is about. TESTFUNC is nearly always
the same as FUNCNAME. Then it also deals with quite a complex decision
in method_is_overloaded().
I have overlooked this test, and now I'm confused by it. In the
definition of slot_nb_add, it seems to check that tp_as_number->nb_add
== slot_nb_add. Is it (a convoluted way) to check that
tp_as_number->nb_add != NULL? Otherwise, I guess this always hold for
standard library modules, but someone may change tp_as_number->nb_add
for another function?
The partial repetition of the logic, which I think is now nested
(because binary_op1() may have called slot_nb_add) is necessary to
insert the more complex version into the decision tree. But this is
roughly where my ability to visualise the paths runs out.
I have an hypothesis: when binary_op1(v, w, ...) is called:
1) either slotv (v->ob_type->tp_as_number->nb_add) is defined. This
calls v's slot_nb_add. This slot_nb_add may then call either v's
"__add__" or w's "__radd__" using the calls to vectorcall_maybe.
2) or slotv is not defined. In that case we start the whole process
using w's slot_nb_add, which should be stored in
w->ob_type->tp_as_number->nb_add.
In particular, does this mean that if slotv is defined, the if(slotw)
<https://github.com/python/cpython/blob/3.8/Objects/abstract.c#L813>
will never be reached (meaning this if could be changed by an else if)?
Letting the case where w is a subtype of v aside for now, this would
mean that some tests may be performed twice, but only one slot_nb_add
will be called? (of course, this slot_nb_add will be able to call either
its __add__ or the __radd__ of the other)
I would not say they are "defined" by the slot_nb_add function.
Rather, if one or other has been defined (in Python), v.__add__ or
w.__radd__ is called by the single function slot_nb_add. They cannot
be called directly from C, but call_maybe() supplies the mechanism.
A confusing factor is that for types defined in C and filling the
nb_add slot, the slot function (float_add, or whatever) has to be
wrapped by two descriptors that can then sit in the type's dictionary
as "__add__" and "__radd__". In that case these *are* defined by a
wrapped C function, but that function is the function in the
implementation of the type (float_add, say), not slot_nb_add. There
are two kinds of wrapper: one used "Python-side out", so
Python-calling "__add__" leads to nb_add's behaviour, and one "C-side
out" so C-calling via nb_add leads to "__add__".
This makes sense. Thank you!
I *think* it is changed to contain slot_nb_add as defined by the macro.
And then slot_nb_add is able to dispatch to __add__ or __radd__ with
vectorcall_maybe. Thanks!
When this is all clearer (and hopefully accurate), I'll wrap it up in a
blog post or something.
--
Raphaël
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at
https://mail.python.org/archives/list/python-dev@python.org/message/C76K6DUQOGMNVTFJ23JQSK3SHMMZ3GHR/
Code of Conduct: http://python.org/psf/codeofconduct/