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/