Thanks for the explanation! And I will look at updating my blog post.

On Mon, Sep 28, 2020 at 9:14 PM Guido van Rossum <gu...@python.org> wrote:

> On Mon, Sep 28, 2020 at 12:03 PM Brett Cannon <br...@python.org> wrote:
>
>> And the code that makes this happen is (I think)
>> https://github.com/python/cpython/blob/6f8c8320e9eac9bc7a7f653b43506e75916ce8e8/Objects/abstract.c#L797-L798
>> .
>>
>
> Ah, that's much clearer than all the English words written so far here.
> :-) Let me go over this function (binary_op1()) for subtraction, the
> example from your blog.
>
> One piece of magic is that there are no separate `__sub__` and `__rsub__`
> implementations at this level -- the `tp_as_number` struct just has a slot
> named `nb_subtract` that takes two objects and either subtracts them or
> returns NotImplemented.
>
> This means that (**at this level**) there really is no point in calling
> `__rsub__` if the lhs and rhs have the same type, because it would
> literally just call the same `nb_subtract` function with the same arguments
> a second time.
>
> And if the types are different but the functions in `nb_subtract` are
> still the same, again we'd be calling the same function with the same
> arguments twice.
>
> The `nb_subtract` slot for Python classes dispatches to either `__sub__`
> or `__rsub__` in a complicated way. The code is SLOT1BINFULL in
> typeobject.c, which echoes binary_op1():
> https://github.com/python/cpython/blob/b0dfc7581697f20385813582de7e92ba6ba0105f/Objects/typeobject.c#L6340-L6383
>
> That's some macro!
>
> Now, interestingly, this macro may call *both* `left.__sub__(right)` and
> `right.__rsub__(left)`. That is surprising, since there's also logic to
> call left's nb_subtract and right's nb_subtract in binary_op1(). What's up
> with that? Could we come up with an example where `a-b` makes more than two
> calls? For that to happen we'd have to trick binary_op1() into calling
> both. But I think that's impossible, because all Python classes have the
> same identical function in nb_subtract (the function is synthesized using
> SLOT1BIN -> SLOT1BINFULL), and in that case binary_op1() skips the second
> call (the two lines that Brett highlighted!). So we're good here.
>
> But maybe here we have another explanation for why binary_op1() is careful
> to skip the second call. (The slot function duplicates this logic so it
> will only call `__sub__` in this case.)
>
> Since rich comparison doesn't have this safeguard, can we trick *that*
> into making more than two calls? No, because the "reverse" logic
> (`self.__lt__(other)` -> `other.__gt__(self)` etc.) is only implemented
> once, in do_richcompare() in abstract.c. The slot function in typeobject.c
> (slot_tp_richcompare()) is totally tame.
>
> So the difference goes back to the design at the C level -- the number
> slots don't have separate `__sub__` and `__rsub__` implementations (the C
> function in nb_subtract has no direct way of knowing if it was called on
> behalf of its first or second argument), and the complications derive from
> that. The rich comparison slot has a clear `op` flag that always tells it
> which operation was requested, and the implementation is much saner because
> of it.
>
> So yes, in a sense the difference is because rich comparison is much newer
> than binary operators in Python -- binary operators are still constrained
> by the original design, which predates operator overloading in user code
> (i.e. `__sub__` and `rsub__`). But it was not a matter of forgetting
> anything -- it was a matter of better design.
>
> (Brett, maybe this warrants an update to your blog post?)
>
> --
> --Guido van Rossum (python.org/~guido)
> *Pronouns: he/him **(why is my pronoun here?)*
> <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
>
_______________________________________________
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/LXQNM6H36TEGNW3CBUPTYL7EYFXGEAUC/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to