Author josh.r
Recipients Eric.Wieser, Stephan Hoyer, josh.r, mark.dickinson, rhettinger
Date 2017-04-25.20:15:25
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1493151325.4.0.0050226098726.issue30140@psf.upfronthosting.co.za>
In-reply-to
Content
I'd assume the preference for __rop__ only on subclass overload is because __rop__ method are usually fallback methods, and differ behaviorally from the __op__ methods in type strictness.

In particular, the __rop__ fallbacks are often so non-strict that they return a completely different type; fractions.Fraction.__rop__ is willing to coerce itself and the other operand to float and produce a float result if the other operand is a numbers.Real (and not a Rational). They also tend to be slower (checking against ABCs and doing more type coercion) than the __op__ path.

If you jump straight to __rop__ because the right hand side is a subclass, but the subclass didn't overload it, you end up going through that fallback, assumed extra liberal and slow, code path.

It doesn't work this way with comparison operators because those are 100% reflexive; there is no expectation that comparing in one direction will be more or less type permissive than comparing in the other direction (stuff like __rcmp__ has been gone for ages after all), so unconditionally comparing using the child class comparator first is fine, and more likely to get correct results.

The design pattern that has problems here is a bit unorthodox to start with. It assumes that the child class constructor will work exactly the same as the parent (no additional mandatory arguments for instance), and that it's always correct for parent + child to produce the type of child. Usually, in an OO design, the parent is not supposed to have any specific knowledge of children; it's the job of the children to work with instances of the parent, if necessary. If delegation to the child is desired, implement __op__ with stricter type checking (to preclude subclasses) and __rop__ with relaxed type checking (to allow them); when the __op__ executes, it will return NotImplemented for the child class, then delegate to __rop__, which will use the child's type.
History
Date User Action Args
2017-04-25 20:15:25josh.rsetrecipients: + josh.r, rhettinger, mark.dickinson, Eric.Wieser, Stephan Hoyer
2017-04-25 20:15:25josh.rsetmessageid: <1493151325.4.0.0050226098726.issue30140@psf.upfronthosting.co.za>
2017-04-25 20:15:25josh.rlinkissue30140 messages
2017-04-25 20:15:25josh.rcreate