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/

Reply via email to