This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: subclassing complex
Type: behavior Stage:
Components: Interpreter Core Versions: Python 2.7, Python 2.6
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: georg.brandl Nosy List: ajaksu2, georg.brandl, gumtree, mark.dickinson
Priority: normal Keywords: patch

Created on 2008-08-29 22:39 by gumtree, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
issue3734.patch mark.dickinson, 2009-02-10 23:31
Messages (8)
msg72169 - (view) Author: Blair (gumtree) Date: 2008-08-29 22:39
The following is quoted from the Python docs (ref/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."

My issue is that the built-in complex type does not respect this rule
(see code below). Note that float can be subclassed using the method
shown below and the rules are applied correctly. It seems that it is
only a problem with complex.

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) ) 

print type(z + xz)  # gives complex when xcomplex is required
msg81571 - (view) Author: Daniel Diniz (ajaksu2) * (Python triager) Date: 2009-02-10 18:12
Confirmed in trunk. Here's a copy-n-past'able testcase:


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) ) 

class xfloat(float):
    def __new__(cls,*args,**kwargs):
        return float.__new__(cls,*args,**kwargs)
    def __coerce__(self,other):
        t = float.__coerce__(self,other)
        try:
            return (self,float(t[1]))
        except TypeError:
            return t
    def __add__(self,x):
        return xfloat( float.__add__(self,x) )
    def __radd__(self,x):
        return xfloat( float.__radd__(self,x) )

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

print type(z + xz)
print type(f + xf)
msg81584 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-02-10 19:21
I'll take a look.
msg81609 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-02-10 22:23
So there's a hint about what's happening here at the bottom of the section

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

of the docs, where it says:

"""In the current implementation, the built-in numeric types int, long and 
float do not use coercion; the type complex however does use it. The 
difference can become apparent when subclassing these types. Over time, 
the type complex may be fixed to avoid coercion. [...]"""

In the OPs example, when z + xz is evaluated, xz is (successfully) coerced 
to type complex, and then complex.__add__ is called to do the addition as 
usual.   There's definitely (at least) a documentation bug here, given 
that at the same place as referenced above it says:

"""New-style classes (those derived from object) never invoke the 
__coerce__() method in response to a binary operator; the only time 
__coerce__() is invoked is when the built-in function coerce() is 
called."""

At the level of the C code, the practical difference between complex and 
(for example) float is that the float type has Py_TPFLAGS_CHECKTYPES set, 
and all arithmetic operations do their own conversions.  The complex type 
doesn't set Py_TPFLAGS_CHECKTYPES, and so (contrary to the documentation) 
relies on complex_coerce to do any conversions.
msg81613 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-02-10 23:31
I think this issue comes down to a doc fix, though I've opened issue 5211 
for anyone who fancies having a go at removing coercion from the complex 
type.

Here's a doc patch.
msg81614 - (view) Author: Blair (gumtree) Date: 2009-02-11 00:00
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.
msg81634 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2009-02-11 08:58
[gumtree]
> [...] 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.

Please see issue 5211 for further discussion.
msg81902 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2009-02-13 10:45
Applied doc patch in r69573.
History
Date User Action Args
2022-04-11 14:56:38adminsetgithub: 47984
2009-02-13 10:45:04georg.brandlsetstatus: open -> closed
resolution: fixed
messages: + msg81902
2009-02-11 08:58:41mark.dickinsonsetmessages: + msg81634
2009-02-11 00:00:05gumtreesetmessages: + msg81614
2009-02-10 23:31:57mark.dickinsonsetfiles: + issue3734.patch
assignee: mark.dickinson -> georg.brandl
messages: + msg81613
keywords: + patch
nosy: + georg.brandl
2009-02-10 22:23:05mark.dickinsonsetmessages: + msg81609
2009-02-10 19:21:52mark.dickinsonsetassignee: mark.dickinson
messages: + msg81584
2009-02-10 18:12:32ajaksu2setnosy: + mark.dickinson, ajaksu2
messages: + msg81571
components: + Interpreter Core, - None
versions: + Python 2.6, Python 2.7, - Python 2.5
2008-08-29 22:39:09gumtreecreate