11

When I try to override the magic method __eq__, and use super to access the base method found in object, I get an error. There's no way this is a bug, but it sure feels like one:

class A(object):
    def __eq__(self, other):
        return super(A, self).__eq__(other)
A() == 0

# raises AttributeError: 'super' object has no attribute '__eq__'

This is unintuitive because object.__eq__ exists, but for class A(object): pass it doesn't. If I'm not mistaken __eq__ resorts to an is check, so that may be the workaround here, but using is instead of super isn't mixin friendly. Going that route is ok in my case, but in others it might not be.

Any suggestions, or info on why __eq__ works this way would be great.

donkopotamus
  • 22,114
  • 2
  • 48
  • 60
rmorshea
  • 832
  • 1
  • 7
  • 25
  • 1
    The error is raised in 2.7 but not raised in 3.5 – hilberts_drinking_problem May 23 '16 at 01:45
  • 1
    No, `object` does not support `__eq__` on **instances** ... try `object().__eq__`, it will raise an `AttributeError` ... instead `object.__eq__ is (probably) a classmethod for checking if types are identical (eg `object.__eq__(object)`) – donkopotamus May 23 '16 at 01:46
  • @donkopotamus: That will not compare your object with `other`; it will compare a newly-created "blank" object with `other`. – BrenBarn May 23 '16 at 01:47
  • @BrenBarn the point I was making is that `object().__eq__` doesn't even work, it will raise an attribute error (edited to make it clear) – donkopotamus May 23 '16 at 01:48

2 Answers2

4

As noted in Will's answer, object() does not actually implement __eq__ at all for instances (in python 2.7).

You are being deceived by the fact that object.__eq__ exists into believing it must be a method that checks if instances of object are equal

Instead, object.__eq__ is actually a class method, inherited from type, that is used to check if types are equal.

That is, to handle expressions such as object == int and object == object.

donkopotamus
  • 22,114
  • 2
  • 48
  • 60
2

This is because object() doesn't actually implement an __eq__(). The "default" here would be:

class A(object):
    def __eq__(self, other):
        if self is other:
            return True

        return self == other

But, if what you're attempting had worked, you would actually be checking whether the self instance of the parent class equaled other. And, it's the same object (self), so it would. So the closest equivalent of your code would actually be:

class A(object):
    def __eq__(self, other):
        if self is other:
            return True

        return super(A, self) == other
Will
  • 24,082
  • 14
  • 97
  • 108
  • 2
    As I commented on a previously-deleted answer, your second example does not inherit the default behavior, since if you do `x = A()`, then `x == x` returns False. (Your first example causes infinite recursion.) – BrenBarn May 23 '16 at 01:46