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

delegation of != to the right-hand side argument is not always done #65607

Closed
exarkun mannequin opened this issue May 1, 2014 · 15 comments
Closed

delegation of != to the right-hand side argument is not always done #65607

exarkun mannequin opened this issue May 1, 2014 · 15 comments
Assignees
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error

Comments

@exarkun
Copy link
Mannequin

exarkun mannequin commented May 1, 2014

BPO 21408
Nosy @benjaminp, @bitdancer, @florentx, @vadmium, @serhiy-storchaka, @MojoVampire
Files
  • method-not-operator.patch
  • method-not-operator-2.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/serhiy-storchaka'
    closed_at = <Date 2015-01-26.08:16:19.111>
    created_at = <Date 2014-05-01.14:31:27.553>
    labels = ['interpreter-core', 'type-bug']
    title = 'delegation of `!=` to the right-hand side argument is not always done'
    updated_at = <Date 2015-01-31.10:24:05.075>
    user = 'https://bugs.python.org/exarkun'

    bugs.python.org fields:

    activity = <Date 2015-01-31.10:24:05.075>
    actor = 'python-dev'
    assignee = 'serhiy.storchaka'
    closed = True
    closed_date = <Date 2015-01-26.08:16:19.111>
    closer = 'serhiy.storchaka'
    components = ['Interpreter Core']
    creation = <Date 2014-05-01.14:31:27.553>
    creator = 'exarkun'
    dependencies = []
    files = ['37672', '37837']
    hgrepos = []
    issue_num = 21408
    keywords = ['patch']
    message_count = 15.0
    messages = ['217699', '217700', '217727', '217729', '217730', '217731', '217732', '233827', '233837', '234618', '234620', '234700', '234718', '234720', '235101']
    nosy_count = 9.0
    nosy_names = ['exarkun', 'benjamin.peterson', 'Arfrever', 'r.david.murray', 'flox', 'python-dev', 'martin.panter', 'serhiy.storchaka', 'josh.r']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue21408'
    versions = ['Python 3.4', 'Python 3.5']

    @exarkun
    Copy link
    Mannequin Author

    exarkun mannequin commented May 1, 2014

    $ ~/Projects/cpython/3.4/python -c '
    class Foo(object):
        def __ne__(self, other):
            return "yup"
        def __eq__(self, other):
            return "nope"
    class Bar(object):
        pass
            
    print(object() != Foo(), object() == Foo())
    print(Bar() != Foo(), Bar() == Foo())
    '
    yup nope
    False nope
    $

    The output I would expect from this is

    yup nope
    yup nope
    

    That is, even when the type of the left-hand argument is not a base class of the type of the right-hand argument, delegation to the right-hand argument is sensible if the left-hand argument does not implement the comparison.

    Note that the output also demonstrates that this is already the behavior for ==. Only != seems to suffer from this issue.

    @exarkun exarkun mannequin added interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error labels May 1, 2014
    @benjaminp
    Copy link
    Contributor

    That's because the implicit default __ne__ on Bar returns the opposite of __eq__.

    @MojoVampire
    Copy link
    Mannequin

    MojoVampire mannequin commented May 1, 2014

    Why would an subclass of object that doesn't redefine either __eq__ or __ne__ have a different behavior for inequality than object itself? Bar never defined __eq__, so it shouldn't have an implicit __ne__ any more than object itself does...

    Saying that Bar has an implicit __ne__ that object doesn't is answering how this happens, but it's not a why; is there a reason why this should be the case, or is this a bug (either in spec or in code) that should be fixed?

    @benjaminp
    Copy link
    Contributor

    The reason object() != Foo() "works" is that Foo is a subtype of object(), so the specialized ne of Foo is called immediately without trying object.ne.

    I don't know whether it's a bug.

    @bitdancer
    Copy link
    Member

    I don't think it's a bug. The subclass-goes-first behavior is very intentional. The implicit __ne__ returning the boolean inverse of __eq__ is what fooled me when I looked at it.

    Or did you mean that following the subclass rule in the case where object is the other class is possibly suspect?

    @benjaminp
    Copy link
    Contributor

    The subclass behavior is a red herring.

    I meant maybe object.ne should check if the other object has a ne method before falling back on not object.__eq__().

    @bitdancer
    Copy link
    Member

    Oh, I see. Yes, that would seem more consistent.

    @vadmium
    Copy link
    Member

    vadmium commented Jan 11, 2015

    There is a bit of analysis of the object.__ne__() implementation in bpo-4395. If my understanding is correct, I think it is a bug that object.__ne__(self, other) evaluates “not self == other”. It should evaluate “not self.__eq__(other)” instead, so that NotImplemented can be caught, allowing the reflected other.__ne__(self) method to be tried.

    @vadmium
    Copy link
    Member

    vadmium commented Jan 11, 2015

    This patch should fix the problem I think. Before the __ne__() implementation was calling the “==” operator; now it calls the __eq__() method instead.

    Also includes extra test for bpo-4395 to avoid having conficting patches.

    @serhiy-storchaka
    Copy link
    Member

    Particular case of this bug:

    >>> class A:
    ...     def __eq__(self, other): return NotImplemented
    ... 
    >>> A().__eq__(object())
    NotImplemented
    >>> A().__ne__(object())
    True

    The second result should be NotImplemented.

    Martin's patch LGTM except few style nitpicks to tests.

    @serhiy-storchaka serhiy-storchaka self-assigned this Jan 24, 2015
    @serhiy-storchaka
    Copy link
    Member

    There are few incorrect implementations of __ne__ in the stdlib. Updated patch removes them. May be we should remove all implementations of __ne__ which are redundant now.

    @vadmium
    Copy link
    Member

    vadmium commented Jan 25, 2015

    I looked over your __ne__ removals from the library, and they all seem sensible to me.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Jan 26, 2015

    New changeset e516badfd3b2 by Serhiy Storchaka in branch '3.4':
    Issue bpo-21408: The default __ne__() now returns NotImplemented if __eq__()
    https://hg.python.org/cpython/rev/e516badfd3b2

    New changeset 7e9880052401 by Serhiy Storchaka in branch 'default':
    Issue bpo-21408: The default __ne__() now returns NotImplemented if __eq__()
    https://hg.python.org/cpython/rev/7e9880052401

    @serhiy-storchaka
    Copy link
    Member

    Thank you for your contribution Martin.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Jan 31, 2015

    New changeset 3603bae63c13 by Serhiy Storchaka in branch 'default':
    Issue bpo-23326: Removed __ne__ implementations. Since fixing default __ne__
    https://hg.python.org/cpython/rev/3603bae63c13

    @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

    4 participants