On Sun, Sep 27, 2020 at 9:56 PM Guido van Rossum <gu...@python.org> wrote:
> On Sun, Sep 27, 2020 at 5:58 PM Brett Cannon <br...@python.org> wrote: > >> >> >> On Sun, Sep 27, 2020 at 2:58 PM Guido van Rossum <gu...@python.org> >> wrote: >> >>> Hm... IIRC the reason why we did this for `__r*__` is because the more >>> derived class might want to return an instance of that class, and we can't >>> assume that the less derived class knows how to create an instance of the >>> more derived class (the `__init__` signatures might differ). >>> >> >> Yep, that's what the data model docs suggest (see the note at >> https://docs.python.org/3/reference/datamodel.html#object.__ror__). >> >> But the interesting bit is skipping the call of __r*__ when `lhs.__r*__ >> == rhs.__r*__` (as long as the derived class requirements are met). That's >> the difference that I'm really curious about compared to rich comparisons >> and their inverse which don't have this call avoidance. >> >> To help visualize all of this, you can see >> https://github.com/brettcannon/desugar/blob/066f16c00a2c78784bfb18eec31476df045cefe5/desugar/operator.py#L93-L103 >> for binary arithmetic operators compared to >> https://github.com/brettcannon/desugar/blob/066f16c00a2c78784bfb18eec31476df045cefe5/desugar/operator.py#L273-L280 >> for rich comparisons. >> > > Ooh, interesting. (Aren't you missing a few checks for MISSING in the elif > or else branches?) > Nope, I handle that generically in the `for` loop farther down that makes the actual calls. That one _MISSING check is because since it's just an instance of `object()` then that subclass check will always succeed. I should probably just define a custom singleton class to let me drop that one guard case. > > Let me guess some more (I'm on a rare caffeine high since 9am so we'll see > how this goes :-). > > The idea is clearly that if lhs and rhs are the same class we don't bother > calling `__r*__` (because if `__*__` didn't do it there's no reason that > `__r*__` would be any different). > That was my assumption. > > Are you sure you read things right, and `__r*__` is skipped when the > `__r*__` methods are the same, and not only when the lhs and rhs classes > are the same? > The test I wrote for this is at https://github.com/brettcannon/desugar/blob/066f16c00a2c78784bfb18eec31476df045cefe5/tests/operator/test_binary_arithmetic.py#L92-L105 and passes when run against CPython via the 'operator' module which just delegates to the syntax anyway. And the code that makes this happen is (I think) https://github.com/python/cpython/blob/6f8c8320e9eac9bc7a7f653b43506e75916ce8e8/Objects/abstract.c#L797-L798 . BTW I have this all linked and written down in https://snarky.ca/unravelling-binary-arithmetic-operations-in-python/. > > It does seem kind of a pointless optimization, since if the first call is > successful we'll skip the second call anyway, and if it returns > NotImplemented, well, if our assumption that `__r*__` is going to do the > same, it's going to be an error anyway. I wonder if this was always there? > Maybe we should study the git blame some more. > Assuming I have the write line of C doe, it looks like you introduced it in 2.2 with new-style classes, 19 years ago to the day. 😄 https://github.com/python/cpython/commit/4bb1e36eec19b30f2e582fceffa250e1598262e1 > > And why don't we do this for rich comparisons? Probably because the logic > is completely separate. :-( And maybe when we did rich comparisons (nearly > a decade after the original binary operator overloading IIRC) the > optimization idea didn't occur to us, or maybe we realized that we'd be > optimizing an error case. Or maybe because rich comparisons were trying to > somehow model the earlier `__cmp__`? > My guess was no one honestly knew/remembered this quirk existed for binary arithmetic operators who were involved with rich comparisons. > > >> [SNIP] >>> I think we could try to change it but it would require a very careful >>> risk analysis. >>> >> >> I'm not sure how critical it is to change. I'm sure there's some >> potential perf gain by avoiding the (potentially) unnecessary call, but I >> also don't know if people have implemented these functions in such a way >> that skipping the inverse operation on the right-hand side object would >> break something. Would abuse of the syntax make a difference (e.g. making >> `>` do something magical)? >> > > I don't know, PEP 207 explicitly says the reflexivity assumptions are > assumed. I guess I misunderstood your question for clarification as a > suggestion to change. > I'm just looking for historical context for a blog post is all. If we feel it's worth considering making the logic more uniform across operators then I think that's worth considering, but I am personally okay considering this a historical quirk that this difference exists to begin with. > > I feel this requires more careful thought than I can muster tonight. > 😄 Yeah, this is definitely digging into the bowels of Python. -Brett > > >> -Brett >> >> >>> >>> On Sun, Sep 27, 2020 at 1:41 PM Brett Cannon <br...@python.org> wrote: >>> >>>> When you do a binary arithmetic operation, one of the things that >>>> dictates whether the left-hand side's __*__ method is called before the >>>> right-hand side's __r*__ method is if the left-hand side's __r*__ differs >>>> (there's also the fact __r*__ methods are not called if. the types are the >>>> same). Presumably this is because you only care about giving precedence to >>>> the right-hand side when it would actually matter due to a difference in >>>> implementation (with the assumption that there isn't a specific need to get >>>> the right-hand side special dispensation to participate in the operation). >>>> >>>> But with rich comparisons there doesn't seem to be an equivalent check >>>> for a difference in method implementation. Why is that? Is it because we >>>> don't want to assume that if someone bothered to implement both __gt__ and >>>> __lt__ that they would not necessarily be the inverse of each other like >>>> __add__ and __radd__? >>>> _______________________________________________ >>>> 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/7NZUCODEAPQFMRFXYRMGJXDSIS3WJYIV/ >>>> Code of Conduct: http://python.org/psf/codeofconduct/ >>>> >>> >>> >>> -- >>> --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/> >>> >> > > -- > --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/E7VYSV7PZ6TEGHHXIILLXLIHSAWKCTNZ/ Code of Conduct: http://python.org/psf/codeofconduct/