classification
Title: Fix complex type to avoid coercion in 2.7.
Type: enhancement Stage: resolved
Components: Interpreter Core Versions: Python 2.7
process
Status: closed Resolution: accepted
Dependencies: Superseder:
Assigned To: mark.dickinson Nosy List: gumtree, mark.dickinson, meador.inge
Priority: low Keywords: easy

Created on 2009-02-10 23:19 by mark.dickinson, last changed 2010-11-28 22:40 by gumtree. This issue is now closed.

Files
File name Uploaded Description Edit
issue-5211-patch meador.inge, 2010-02-06 15:11 patch against 2.7 trunk
unnamed gumtree, 2010-11-28 22:40
Messages (25)
msg81612 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-02-10 23:19
In the 'coercion rules' section of the reference manual, at:

http://docs.python.org/dev/reference/datamodel.html#id5

it says:

"""Over time, the type complex may be fixed to avoid coercion."""

In 3.x, the complex type has (necessarily) been fixed to avoid coercion, 
and it ought to be a fairly easy task to backport that fix to 2.7, for 
someone who wants to get his or her feet wet with some CPython hacking.

As far as I can see, there's no great benefit in such a change, except 
that the presence of coercion for the complex type causes confusion 
occasionally: see issue 3734 for an example of this.
msg81633 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-02-11 08:56
Comment by gumtree copied from issue3734 discussion:

> While Mark Dickinson's patch fixes the documentation, it does not 
offer 
> a solution to the original problem, which was rooted in a need to 
> provide special behaviour based on the numeric types. I made the 
> original posting because I hoped that this problem could be resolved.

gumtree, would you be interested in working on a patch for this feature-
request?  As mentioned above, the necessary changes are already present 
in 3.x, so all that's entailed is figuring out which bits of the 
Object/complexobject.c in the py3k source need to be transferred to the 
trunk, and making sure that everything's properly tested.

Alternatively, could you explain in a little more detail why this change 
is important to you?  Subclassing complex doesn't seem like a very 
common thing to want to do, so I'm curious about your use case.
msg81694 - (view) Author: Blair (gumtree) Date: 2009-02-12 00:40
I am happy to collaborate in finding a solution, but I do not have 
enough knowledge or skill to work with the code alone.

Simply documenting it does not remove the frustration that a few people 
will enounter. The key point being that you can subclass the other 
numeric types, but not complex. Worse, you may think that you have 
succeeded in subclassing complex too, because it is only when the 
__ropn__ binary operators are used that the type reverts to the base 
class type.

Would you want to subclass a numeric type? I agree, it is a bit obsure, 
but I did a search on this before making the post and there have been 
others who found the problem too. 

In my case, I think that the motivation may seem a bit obscure. I had 
developed some numerical-like types (from scratch -- no subclassing) 
and I wanted to be able to write functions taking as possible arguments 
these types and Python numerical types indifferently. I realised that I 
could not achieve exactly what I wanted, however, by subclassing float, 
int, etc I could add a few methods that would allow my generic 
functions to work with either my types or the subclassed Python types. 
At the same time, the subclassed numerical types could still be used as 
numerical quantities (float, int,...). It seemed like a pretty elegant 
solution.

If that explanation does not make sense, then I suppose other simpler 
motivations could be, eg, to subclass float so that only positive 
values are acceptable; to subclass complex so that only values lying 
within the unit circle are acceptable, etc. That is, one might like to 
define a type that can only take on physically meaningful values (mass 
cannot be negative, a complex reflection coeffcieint cannot have a 
magnitude greater than unity, ..)

So, my feeling is that this is worth fixing because the work done on 
float, int etc, is clearly useful and it appears (to me) that the 
complex case is an oversight.
msg98714 - (view) Author: Meador Inge (meador.inge) * (Python committer) Date: 2010-02-02 04:23
Mark,

Is this still of interest?

I found the relevant changes in py3k, but I am not sure it is the behavior that gumtree is expecting.  Since py3k removes coercion completely, the test case from issue 3734 would just issue:

   Traceback (most recent call last):
     File "test-5211.py", line 34, in <module>
       print type(z + xz)
     File "test-5211.py", line 5, in __coerce__
       t = complex.__coerce__(self,other)
   AttributeError: type object 'complex' has no attribute '__coerce__'

Where as I think gumtree wants the xcomplex case to behave as the xfloat case, e.g.
   <class '__main__.xfloat'>
   <class '__main__.xcomplex'>
Since coercion is getting axed in py3k, I don't think it makes sense to provide this behavior.

On the other hand, as you mentioned, the removal of coercion from complex could be backported.  However, if we are going to do that then we might as well just backport the whole removal of coercion instead of just the bits from 'complexobject.c'.  Bascially checkins r51431 and r58226.
msg98784 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-02-03 14:12
Yes, I'd certainly be interested in reviewing a patch.  Though the current behaviour is at most a minor wart, and since it's gone in Python 3.x the motivation to fix it isn't huge. :)

> Where as I think gumtree wants the xcomplex case to behave as
> the xfloat case, e.g. ...

Yes, that was what I was proposing.  But as you point out, the new behaviour wouldn't even match the behaviour of Python 3.x, so it really wouldn't be a terribly useful change.

> However, if we are going to do that then we might as well just
> backport the whole removal of coercion.

That's not really an option:  it has the potential to break existing code that uses coercion.  Removing coercion in Python 2.x would require at least a PEP plus one version's worth of DeprecationWarning.  And given that it currently looks as though Python 2.8 isn't likely to happen at all, that would likely be a wasted effort.
msg98798 - (view) Author: Blair (gumtree) Date: 2010-02-03 20:39
I also agree that this bug was never more than a small wart. However, I'm now curious. 

If Python 3 does not support coercion, I think that it will not be possible to write something like my xfloat class, derived from float (i.e., some binary operations between an xfloat and a float would return a float instead of an xfloat). 

If I am correct in think that it would seem to be a step backward.

Will Python 3 deal with mixed types in some other way, or has this problem been abandoned altogether? If it is the latter, I think it is a pity.
msg98810 - (view) Author: Meador Inge (meador.inge) * (Python committer) Date: 2010-02-04 02:54
> Mark
>
> Yes, that was what I was proposing.

As you pointed out in issue 3734, the patch is basically:

===================================================================
--- Objects/complexobject.c	(revision 77909)
+++ Objects/complexobject.c	(working copy)
@@ -1282,7 +1282,8 @@
 	PyObject_GenericGetAttr,		/* tp_getattro */
 	0,					/* tp_setattro */
 	0,					/* tp_as_buffer */
-	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES 
+                | Py_TPFLAGS_BASETYPE,          /* tp_flags */
 	complex_doc,				/* tp_doc */
 	0,					/* tp_traverse */
 	0,					/* tp_clear */

plus the relevant test cases and documentation changes.

> Mark
>
> That's not really an option:  it has the potential to break existing 
> code that uses coercion.  Removing coercion in Python 2.x would 
> require at least a PEP plus one version's worth of DeprecationWarning.

Ah, good point.  In that case I am -1 on changing the behavior at all.  For the sole reason that cases like Blair originally reported would not work as expected in version < 2.7, then would work in 2.7, and then would break again in > 3.0.  I think explaining and documenting the change in behavior would be really confusing.

> Blair
>
> If I am correct in think that it would seem to be a step backward.

I disagree.  I think type coercion (or implicit conversions) is hard to follow and understand.  I say this from many years of C and C++ programming.  I see bugs all of the time in these languages due to conversions silently happening that the programmer was not aware of.  Yes the feature is convenient, but documenting and explaining these rules to programmers becomes very tedious and requires that most programmers have deep understanding of the languages typing rules.  This typically leads to books like "Effective C++" that catalog a list of things *not* to do in a language.  The relevant case for C++ being to avoid user defined conversions and use explicit constructers.  As such, I am definitely +1 on removing coercion.
msg98811 - (view) Author: Blair (gumtree) Date: 2010-02-04 03:48
OK. I have gone back to the beginning to refresh my memory and I see a possible point of misunderstanding. I am not sure that we are really talking about the problem that prompted my initial report (msg72169, issue 3734). 

Immediately following my message, Daniel Diniz confirmed the bug and expanded on my code with an xfloat class of his own that uses __coerce__. 

In fact, if I had submitted an xfloat class it would have been the following 

class xfloat( float ):

    def __new__(cls,*args,**kwargs):
        return float.__new__(cls,*args,**kwargs)
        
    def __add__(self,x):
        return xfloat( float.__add__(self,x) )

    def __radd__(self,x):
        return xfloat( float.__radd__(self,x) )

My xfloat works fine in 2.6.4 and it was my wish, at the time, to write a class for xcomplex that behaved in a similar way. According to the Python manual, that should have been possible, but it wasn't.

So, I guess coercion is not really the problem. 

However, there does seem to be something wrong with the complex type.

I have looked at the manual for Python 3 and see that the same rules apply for classes that emulate numeric types, namely:

"If the right operand’s type is a subclass of the left operand’s type and that subclass provides the reflected method for the operation, this method will be called before the left operand’s non-reflected method. This behavior allows subclasses to override their ancestors’ operations."

The question I have then is will the following work in Python 3 (it doesn't in 2.6.4)?

class xcomplex( complex ):

    def __new__(cls,*args,**kwargs):
        return complex.__new__(cls,*args,**kwargs)

##    def __coerce__(self,other):
##        t = complex.__coerce__(self,other)
##        try:
##            return (self,xcomplex(t[1]))
##        except TypeError:
##            return t
        
    def __add__(self,x):
        return xcomplex( complex.__add__(self,x) )
    
    def __radd__(self,x):
        return xcomplex( complex.__radd__(self,x) )

xz = xcomplex(1+2j)
xy = float(10.0)
z = complex(10+1j)

print "would like xcomplex type each time"
print type(xz + z)  
print type(xz + xy) 
print type(xz + 10) 
print type(xy + xz) 
print type(10 + xz) 
print type(z + xz)
msg98828 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-02-04 11:48
Blair:  I don't think you'll have any problems getting the behaviour you in Python 3.  For example:

Python 3.2a0 (py3k:77952, Feb  4 2010, 10:56:12) 
[GCC 4.2.1 (Apple Inc. build 5646) (dot 1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class xcomplex(complex):
...     def __add__(self, other): return xcomplex(complex(self) + other)
...     __radd__ = __add__
... 
>>> xz = xcomplex(1+2j)
>>> all(type(xz + y) is type(y + xz) is xcomplex for y in (1, 10.0, 10+1j, xz))
True

So I don't consider that the removal of coerce and the __coerce__ magic method is a step backward:  it still allows mixed-type operations, but without coerce the rules for those operations are significantly cleaner.

The real problem case in 2.6.4 seems to be when doing <instance of complex> + <instance of xcomplex>:  in this case, Python first calls complex.__coerce__ (which returns its arguments unchanged), then complex.__add__.  None of the xcomplex methods even gets a look in, so there's no opportunity to force the return type to be xcomplex.

If you look at the source (see particularly the binary_op1 function in Objects/abstract.c ) you can see where this behaviour is coming from.  The complex type (along with its subclasses) is classified as an 'old-style' number, while ints, longs and floats are 'new-style' (note that this has nothing to do with the distinction between old-style and new-style classes).  Operations between new-style numbers use the scheme described in the documentation, but where old-style numbers are involved there's an extra coercion step.  In particular, when adding a complex to an xcomplex, the rule you quoted (about the case when the right operand is an instance of a subclass of the class of the left operand) isn't applied.

It's too risky to change the general behaviour for old-style numbers:  I don't think there are any old-style numbers besides complex in the Python code, but there may well be some in third party extensions.  Certainly documenting it better would be an option, and making complex a new-style number for Python 2.7 seems like a reasonable thing to consider.
msg98829 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-02-04 11:52
> As you pointed out in issue 3734, the patch is basically:
> <snipped patch that adds Py_TPFLAGS_CHECKTYPES>

Yes, that's the essence of it.  In addition, each of the functions implementing a complex special method would need to do its own argument conversion.  (Compare the implementation of complex_add in py3k with that in trunk.)
msg98830 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-02-04 11:59
> Yes, that was what I was proposing.  But as you point out, the new
> behaviour wouldn't even match the behaviour of Python 3.x, so it really
> wouldn't be a terribly useful change.

Hmm.  I take this back:  if complex were made 'new-style' in 2.7, then it *would* be possible to write fairly obvious code (not using coerce or __coerce__) that operated in the same way in both 2.7 and 3.2.  So I still think it's worth considering.
msg98941 - (view) Author: Meador Inge (meador.inge) * (Python committer) Date: 2010-02-06 15:11
> if complex were made 'new-style' in 2.7, then it *would* be possible to > write fairly obvious code (not using coerce or __coerce__) that operated > in the same way in both 2.7 and 3.2.  So I still think it's worth 
> considering.

Agreed.  I have attached a patch with src, test, and doc changes.
msg98971 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-02-06 23:26
Thanks.  I'll try to find time to look at this tomorrow.
msg99653 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-02-21 12:58
Apologies for the delay;  tomorrow was a long time coming...

The patch looks great---thank you!  I added a ".. versionchanged" note to the documentation, and fixed a couple of whitespace issues; apart from that I didn't change anything.  Applied in r78280.
msg99691 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-02-21 22:15
I must have been operating on autopilot;  not only did I forget to put the issue number in the checkin message, but I forgot to acknowledge Meador Inge for the patch.  Fixed now, with apologies to Meador.
msg99697 - (view) Author: Meador Inge (meador.inge) * (Python committer) Date: 2010-02-22 00:30
> I added a ".. versionchanged" note to the documentation, and fixed a 
> couple of whitespace issues;

Thanks.  I checked out the changes you made so that I will know what to do next time :).

> Fixed now, with apologies to Meador.

No worries.  Thanks for applying the patch!
msg106757 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-05-30 12:13
r78280 didn't remove the implicit coercion for rich comparisons;  that's now been done in r81606.
msg122432 - (view) Author: Blair (gumtree) Date: 2010-11-26 03:45
Hi Mark,

I thought that this had all been fixed, but it seems not.

Consider the following:

class xcomplex( complex ):
    def __new__(cls,*args,**kwargs):
        return complex.__new__(cls,*args,**kwargs)
    def __add__(self,x):
        return xcomplex( complex.__add__(self,x) )
    def __radd__(self,x):
        print "larg: ", type(x),"returning: ",
        return xcomplex( complex.__radd__(self,x) )

class xfloat(float):
    def __new__(cls,*args,**kwargs):
        return float.__new__(cls,*args,**kwargs)
    def __add__(self,x):
        return xfloat( float.__add__(self,x) )
    def __radd__(self,x):
        print "larg: ", type(x),"returning: ",
        return xfloat( float.__radd__(self,x) )

z = 1j
xz = xcomplex(1j)
f = 1.0
xf = xfloat(1.0)

print
print "-----------"
print "expect xcomplex:", type(z + xz)
print "expect xcomplex:", type(f + xz)
print "expect xfloat:", type(f + xf)
print "expect ???:", type(z + xf)

When this runs, the first three conversions are fine, the last is not: there
is no call to xfloat.__radd__. It seems that the builtin complex type simply
thinks it is dealing with a float. Here is the output

-----------
expect xcomplex: larg:  <type 'complex'> returning:  <class
'__main__.xcomplex'>
expect xcomplex: larg:  <type 'float'> returning:  <class
'__main__.xcomplex'>
expect xfloat: larg:  <type 'float'> returning:  <class '__main__.xfloat'>
expect ???: <type 'complex'>

The last line shows that no call to __radd__ occurred.

Is there anything that can be done now about this now, or is it just too
late?

Regards

Blair

At 01:13 a.m. 31/05/2010, you wrote:

Mark Dickinson <dickinsm@gmail.com> added the comment:

r78280 didn't remove the implicit coercion for rich comparisons;  that's now
been done in r81606.

----------

_______________________________________
Python tracker <report@bugs.python.org>
<http://bugs.python.org/issue5211>
_______________________________________Python tracker <
report@bugs.python.org>
msg122453 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-11-26 13:12
I think that's expected behaviour.  Note that int vs float behaves in the same way as float vs complex:

>>> class xint(int):
...     def __radd__(self, other):
...         print "__radd__"
...         return 42
... 
>>> 3 + xint(5)
__radd__
42
>>> 3.0 + xint(5)  # xint.__radd__ not called.
8.0

As with your example, the float.__add__ method is happy to deal with an int or an instance of any subclass of int.
msg122472 - (view) Author: Blair (gumtree) Date: 2010-11-26 18:41
I see your point Mark, however it does not seem to be the right way to do
this.

Are you aware that Python has formally specified this behaviour somewhere? I
could not find an explicit reference in the documentation.

The problem that has been fixed is covered in the documentation:

(3.4.8. Emulating numeric types: Note
If the right operand’s type is a subclass of the left operand’s type and
that subclass provides the
reflected method for the operation, this method will be called before the
left operand’s non-reflected method.
This behavior allows subclasses to override their ancestors’ operations.)

This rule is needed so that mixed-type arithmetic operations do not revert
to the ancestor's type. However, one would expect that different numeric
types (int float complex)  would all behave in a similar way. For example,

xi = xint(3)
3 + xi  # is an xint(6)
3.0 + xi # is float(6)

This is the same problem as the one that has been fixed from a practical
point of view. Such behaviour is not going to be useful (IMO).

It seems to me that xint.__radd__ would need to be called if the left
operand is a subclass of any of the number types (in this case,
isinstance(left_op,numbers.Complex) == True).

Am I missing something?

Mark Dickinson <dickinsm@gmail.com> added the comment:

I think that's expected behaviour.  Note that int vs float behaves in the
same way as float vs complex:

...     def __radd__(self, other):
...         print "__radd__"
...         return 42
...
>>> 3 + xint(5)
__radd__
42
>>> 3.0 + xint(5)  # xint.__radd__ not called.
8.0

As with your example, the float.__add__ method is happy to deal with an int
or an instance of any subclass of int.

----------

_______________________________________
Python tracker <report@bugs.python.org>
<http://bugs.python.org/issue5211>
_______________________________________
msg122499 - (view) Author: Blair (gumtree) Date: 2010-11-27 02:05
I'd like to add a few more observations to the mix.

I have run the following in both 2.6.6 and in 2.7

class xfloat(float):
    def __new__(cls,x):
        return float.__new__(cls,x)

    def __radd__(self,lhs):
        print "__radd__ got: %s" % type(lhs)
        if isinstance(lhs,(int,float)):
            return xfloat( float(self) + lhs )
        else:
            return NotImplemented

xf = xfloat(9.0)

cases = dict(int=1,float=1.0,complex=1.0+1j)
for k,v in cases.items():
    y = v + xf
    print "%s + xfloat" % k
    print type(y)
    print y

In 2.7 this gives:

__radd__ got: <type 'int'>
int + xfloat
<class '__main__.xfloat'>
10.0
__radd__ got: <type 'float'>
float + xfloat
<class '__main__.xfloat'>
10.0
complex + xfloat
<type 'complex'>
(10+1j)

In 2.6.6 I get:

__radd__ got: <type 'int'>
int + xfloat
<class '__main__.xfloat'>
10.0
__radd__ got: <type 'float'>
float + xfloat
<class '__main__.xfloat'>
10.0
__radd__ got: <type 'complex'>
complex + xfloat
<type 'complex'>
(10+1j)

They are the same except for the last case.

My feeling is that the behaviour of 2.6.6 (for subclassing float) is
correct.

The behaviour of 2.6.6 is needed to enable you to implement the commutative
property of addition (ie, you expect to get the same outcome from x+y or
y+x), which I would say is a pretty fundamental requirement.

I have also tried the following

class xint(int):
    def __new__(cls,x):
        return int.__new__(cls,x)

    def __radd__(self,lhs):
        print "__radd__ got: %s" % type(lhs)
        if isinstance(lhs,(int,)):
            return xint( float(self) + lhs )
        else:
            return NotImplemented

print
print "-------------------"
xf = xint(9)

cases = dict(int=1,float=1.0,complex=1.0+1j)
for k,v in cases.items():
    y = v + xf
    print "%s + xint" % k
    print type(y)
    print y

In 2.6.6 I get

__radd__ got: <type 'int'>
int + xint
<class '__main__.xint'>
10
float + xint
<type 'float'>
10.0
__radd__ got: <type 'complex'>
complex + xint
<type 'complex'>
(10+1j)

and in 2.7

-------------------
__radd__ got: <type 'int'>
int + xint
<class '__main__.xint'>
10
float + xint
<type 'float'>
10.0
complex + xint
<type 'complex'>
(10+1j)

In my opinion, 2.6.6 was faulty in the float + xint case, for the same
reasons as above, and 2.7 is faulty in both float + xint and complex + xint.
msg122659 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-11-28 14:34
> In my opinion, 2.6.6 was faulty in the float + xint case, for the same
> reasons as above, and 2.7 is faulty in both float + xint and complex +
> xint.

Well, I disagree:  Python is behaving as designed and documented in these cases.  If you want to argue that the *design* decisions are the wrong ones, then I'd suggest opening a discussion on the python-ideas mailing list, where more people are likely to get involved---this tracker isn't really the right place for that sort of discussion.

Leaving complex out of the mix for the moment, it sounds to me as though you'd like, e.g.,

<float> + <subclass of int>

to call the int subclass's __radd__ method (if it exists) before calling the float's __add__ method.  Is that correct?

Or are you suggesting that float's __add__ method shouldn't accept instances of subclasses of int at all?  (i.e., that float.__add__ should return NotImplemented when given an instance of xint).

In the first case, you need to come up with general semantics that would give you the behaviour you want for float and xint---e.g., when given numeric objects x and y, what general rule should Python apply to decide whether to call x.__add__ or y.__radd__ first?

In the second case, I'd argue that you're going against the whole idea of object-oriented programming; by making xint a subclass of int, you're declaring that its instances *are* 'ints' in a very real sense, so it's entirely reasonable for float's __add__ method to accept them.

In either case, note that Python 2.x is not open for behaviour changes, only for bugfixes.  Since this isn't a bug (IMO), such changes could only happen in 3.x.

Please take further discussion to the python-ideas mailing list.
msg122724 - (view) Author: Blair (gumtree) Date: 2010-11-28 20:32
Just to keep this discussion as clear as possible Mark, it was your first
option that I suggest is needed.

When that is done (as it was for a subclass of float in 2.6.6) it is
possible for the author of the subclass to implement commutative binary
operations (like + and * that must behave the same regardless of argument
order). Otherwise (as far as I can see) this cannot be done.

On Mon, Nov 29, 2010 at 5:04 AM, Mark Dickinson <report@bugs.python.org>wrote:

>
> Changes by Mark Dickinson <dickinsm@gmail.com>:
>
>
> Removed file: http://bugs.python.org/file19820/unnamed
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue5211>
> _______________________________________
>
msg122727 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-11-28 20:46
> Just to keep this discussion as clear as possible Mark, it was your
> first option that I suggest is needed.

Okay, so you want <float instance> + <xint instance> to try xint.__radd__ before float.__add__.

How do you propose this be achieved?  Can you explain exactly what semantics you'd like to see?  You've indicated what you want to happen for float and xint, but how should Python behave in general here?

In particular, when evaluating 'x + y' for general Python objects x and y, what rule or rules should Python use to decide whether to try x.__add__(y) or y.__radd__(x) first?

It seems you want some general mechanism that results in xint being 'preferred' to float, in the sense that xint.__radd__ and xint.__add__ will be tried in preference to float.__add__ and float.__radd__ (respectively).  But it's not clear to me what criterion Python would use to determine which out of two types (neither one inheriting from the other) should be 'preferred' in this sense.
msg122742 - (view) Author: Blair (gumtree) Date: 2010-11-28 22:40
I am not really the person (I don't know how Python is implemented) to
explain how the correct behaviour should be achieved (sorry). I do
appreciate that this may seem like exceptional behaviour. Numbers are a bit
different.

However, for what its worth, I think that the 'correct behaviour' was
implemented for subclasses of float and was working in Python 2.6, but not
now in 2.7. I don't know how the earlier implementation was done, but it
does work (I have used it to develop a nice little math library). Would
there be any documentation about the implementation?

I would say that the semantics do not need to apply to arbitrary Python
objects. The problem arises for numeric type subclasses when they are mixed
with non-subclassed numeric types.

In that case:

For 'x opn y' any binary operator (like +,*, etc), if (and only if)  'x' is
a built-in numeric type (int, long, float, complex) and 'y' is a subclass of
a built-in numeric type, then y.__ropn__(x) (if it exists) should be called
before x.__opn__(y).

If that were done, then subclasses of number types can implement commutative
operator properties. Otherwise, I don't think it works properly.

I see this as 'special' behaviour required of the int, long, float and
complex classes, rather than special behaviour for all Python objects.

If both 'x' and 'y' are subclasses of built in number types the required
behaviour seems too complicated to specify. I would be inclined to do
nothing special. That is, if both 'x' and 'y' are derived from built in
numeric classes (possibly different types) then x.__opn__(y) would be
called.

And should this apply to non-number types? I think not. Numbers deserve
special treatment.

I hope this helps.

On Mon, Nov 29, 2010 at 9:48 AM, Mark Dickinson <report@bugs.python.org>wrote:

>
> Changes by Mark Dickinson <dickinsm@gmail.com>:
>
>
> Removed file: http://bugs.python.org/file19859/unnamed
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <http://bugs.python.org/issue5211>
> _______________________________________
>
History
Date User Action Args
2010-11-28 22:40:11gumtreesetfiles: + unnamed

messages: + msg122742
2010-11-28 20:48:33mark.dickinsonsetfiles: - unnamed
2010-11-28 20:46:15mark.dickinsonsetmessages: + msg122727
2010-11-28 20:32:29gumtreesetfiles: + unnamed

messages: + msg122724
2010-11-28 16:04:10mark.dickinsonsetfiles: - unnamed
2010-11-28 16:04:00mark.dickinsonsetfiles: - unnamed
2010-11-28 16:03:47mark.dickinsonsetfiles: - unnamed
2010-11-28 14:34:24mark.dickinsonsetmessages: + msg122659
2010-11-27 02:05:06gumtreesetfiles: + unnamed

messages: + msg122499
2010-11-26 18:41:23gumtreesetfiles: + unnamed

messages: + msg122472
2010-11-26 13:12:33mark.dickinsonsetmessages: + msg122453
2010-11-26 03:45:04gumtreesetfiles: + unnamed

messages: + msg122432
2010-05-30 12:13:29mark.dickinsonsetmessages: + msg106757
2010-02-22 00:30:42meador.ingesetmessages: + msg99697
2010-02-21 22:15:01mark.dickinsonsetmessages: + msg99691
2010-02-21 12:58:44mark.dickinsonsetstatus: open -> closed
resolution: accepted
messages: + msg99653

stage: resolved
2010-02-06 23:26:57mark.dickinsonsetassignee: mark.dickinson
messages: + msg98971
2010-02-06 15:11:09meador.ingesetfiles: + issue-5211-patch

messages: + msg98941
2010-02-04 11:59:21mark.dickinsonsetmessages: + msg98830
2010-02-04 11:52:31mark.dickinsonsetmessages: + msg98829
2010-02-04 11:49:00mark.dickinsonsetmessages: + msg98828
2010-02-04 03:48:40gumtreesetmessages: + msg98811
2010-02-04 02:54:39meador.ingesetmessages: + msg98810
2010-02-03 20:39:41gumtreesetmessages: + msg98798
2010-02-03 14:12:08mark.dickinsonsetmessages: + msg98784
2010-02-02 04:23:34meador.ingesetnosy: + meador.inge
messages: + msg98714
2009-02-12 00:40:48gumtreesetmessages: + msg81694
2009-02-11 08:56:53mark.dickinsonsetnosy: + gumtree
messages: + msg81633
2009-02-10 23:19:22mark.dickinsoncreate