Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

subclassing complex #47984

Closed
gumtree mannequin opened this issue Aug 29, 2008 · 8 comments
Closed

subclassing complex #47984

gumtree mannequin opened this issue Aug 29, 2008 · 8 comments
Assignees
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error

Comments

@gumtree
Copy link
Mannequin

gumtree mannequin commented Aug 29, 2008

BPO 3734
Nosy @birkenfeld, @mdickinson, @devdanzin
Files
  • issue3734.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/birkenfeld'
    closed_at = <Date 2009-02-13.10:45:04.809>
    created_at = <Date 2008-08-29.22:39:09.358>
    labels = ['interpreter-core', 'type-bug']
    title = 'subclassing complex'
    updated_at = <Date 2009-02-13.10:45:04.808>
    user = 'https://bugs.python.org/gumtree'

    bugs.python.org fields:

    activity = <Date 2009-02-13.10:45:04.808>
    actor = 'georg.brandl'
    assignee = 'georg.brandl'
    closed = True
    closed_date = <Date 2009-02-13.10:45:04.809>
    closer = 'georg.brandl'
    components = ['Interpreter Core']
    creation = <Date 2008-08-29.22:39:09.358>
    creator = 'gumtree'
    dependencies = []
    files = ['13022']
    hgrepos = []
    issue_num = 3734
    keywords = ['patch']
    message_count = 8.0
    messages = ['72169', '81571', '81584', '81609', '81613', '81614', '81634', '81902']
    nosy_count = 4.0
    nosy_names = ['georg.brandl', 'mark.dickinson', 'ajaksu2', 'gumtree']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = None
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue3734'
    versions = ['Python 2.6', 'Python 2.7']

    @gumtree
    Copy link
    Mannequin Author

    gumtree mannequin commented Aug 29, 2008

    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

    @gumtree gumtree mannequin added the type-bug An unexpected behavior, bug, or error label Aug 29, 2008
    @devdanzin
    Copy link
    Mannequin

    devdanzin mannequin commented Feb 10, 2009

    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)

    @devdanzin devdanzin mannequin added the interpreter-core (Objects, Python, Grammar, and Parser dirs) label Feb 10, 2009
    @mdickinson
    Copy link
    Member

    I'll take a look.

    @mdickinson mdickinson self-assigned this Feb 10, 2009
    @mdickinson
    Copy link
    Member

    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.

    @mdickinson
    Copy link
    Member

    I think this issue comes down to a doc fix, though I've opened bpo-5211
    for anyone who fancies having a go at removing coercion from the complex
    type.

    Here's a doc patch.

    @mdickinson mdickinson assigned birkenfeld and unassigned mdickinson Feb 10, 2009
    @gumtree
    Copy link
    Mannequin Author

    gumtree mannequin commented Feb 11, 2009

    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.

    @mdickinson
    Copy link
    Member

    [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 bpo-5211 for further discussion.

    @birkenfeld
    Copy link
    Member

    Applied doc patch in r69573.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    2 participants