Pergunta

I'm taking my first computing science course, and we just learned about class implementation and inheritance. In particular, we just covered method overriding and how classes we define inherit from the object superclass by default. As one of my examples trying out this particular case of inheritance, I used the following code:

class A:
    def __init__(self, i):
        self.i = i
    def __str__(self):
        return "A"
    # Commenting out these two lines to not override __eq__(), just use the 
    # default from our superclass, object
    #def __eq__(self, other):
        #return self.i == other.i

x = A(2)
y = A(2)

>>>print(x == y)
False
>>>print(x.__eq__(y))
NotImplemented

I expected the result from (x == y), because as I understand it the default for __eq__() is to check if they're the same objects or not, not worrying about the contents. Which is False, x and y have the same contents but are different objects. The second one surprised me though.

So my questions: I thought (x==y) and x.__eq__(y) were synonymous and made exactly the same call. Why do these produce differing output? And why does the second conditional return NotImplemented?

Foi útil?

Solução 2

The NotImplemented value you're seeing returned from your inherited __eq__ method is a special builtin value used as a sentinel in Python. It can be returned by __magic__ methods that implement mathematical or comparison operators to indicate that the class does not support the operator that was attempted (with the provided arguments).

This can be more useful than raising an exception, as it allows Python to fall back to other options to resolve the operator use. For instance, if you do x + y, Python will first try to run x.__add__(y). If that returns NotImplemented, it will next try the "reverse" version, y.__radd__(x), which may work if y is a more sophisticated type than x is.

In the case you're asking about, x == y, Python first tries x.__eq__(y), then y.__eq__(x), and finally x is y (which will always evaluate to a Boolean value). Since object.__eq__ returns NotImplemented in all cases, your class falls back to the identity comparison when you use the real operator, but shows you the NotImplemented sentinel when you call __eq__ directly.

Outras dicas

The == operator is equivalent to the eq function, which will internally call the __eq__ method of the left operand if it exists to try to determine equality. This is not the only thing it will do, and if __eq__ does not exist, as is the case here, it will do other checks, such as checking whether the two are the same object, or __cmp__ pre-Python 3.

So in a nutshell, your confusion arises from this assumption, which is incorrect:

I thought (x==y) and x.__eq__(y) were synonymous and made exactly the same call

In fact, (x==y) and operators.eq(x, y) are synonymous, and x.__eq__(y) is one of the things eq(x, y) will try to check.

If you have implemented the __eq__() function for a class, it gets called when you use x == y. Otherwise x == y relies on a default comparison logic. __eq__() does not get implemented automatically when you define a class.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top