开发者

How is __eq__ handled in Python and in what order?

Since Python does not provide left/right versions of its comparison operators, how does it decide which function to call?

class A(object):
    def __eq__(self, other):
        print "A __eq__ called"
        return self.value == other
class B(object):
    def __eq__(self, othe开发者_Python百科r):
        print "B __eq__ called"
        return self.value == other

>>> a = A()
>>> a.value = 3
>>> b = B()
>>> b.value = 4
>>> a == b
"A __eq__ called"
"B __eq__ called"
False

This seems to call both __eq__ functions.

I am looking for the official decision tree.


The a == b expression invokes A.__eq__, since it exists. Its code includes self.value == other. Since int's don't know how to compare themselves to B's, Python tries invoking B.__eq__ to see if it knows how to compare itself to an int.

If you amend your code to show what values are being compared:

class A(object):
    def __eq__(self, other):
        print("A __eq__ called: %r == %r ?" % (self, other))
        return self.value == other
class B(object):
    def __eq__(self, other):
        print("B __eq__ called: %r == %r ?" % (self, other))
        return self.value == other

a = A()
a.value = 3
b = B()
b.value = 4
a == b

it will print:

A __eq__ called: <__main__.A object at 0x013BA070> == <__main__.B object at 0x013BA090> ?
B __eq__ called: <__main__.B object at 0x013BA090> == 3 ?


When Python2.x sees a == b, it tries the following.

  • If type(b) is a new-style class, and type(b) is a subclass of type(a), and type(b) has overridden __eq__, then the result is b.__eq__(a).
  • If type(a) has overridden __eq__ (that is, type(a).__eq__ isn't object.__eq__), then the result is a.__eq__(b).
  • If type(b) has overridden __eq__, then the result is b.__eq__(a).
  • If none of the above are the case, Python repeats the process looking for __cmp__. If it exists, the objects are equal iff it returns zero.
  • As a final fallback, Python calls object.__eq__(a, b), which is True iff a and b are the same object.

If any of the special methods return NotImplemented, Python acts as though the method didn't exist.

Note that last step carefully: if neither a nor b overloads ==, then a == b is the same as a is b.


From https://eev.ee/blog/2012/03/24/python-faq-equality/


Python 3 Changes/Updates for this algorithm

How is __eq__ handled in Python and in what order?

a == b

It is generally understood, but not always the case, that a == b invokes a.__eq__(b), or type(a).__eq__(a, b).

Explicitly, the order of evaluation is:

  1. if b's type is a strict subclass (not the same type) of a's type and has an __eq__, call it and return the value if the comparison is implemented,
  2. else, if a has __eq__, call it and return it if the comparison is implemented,
  3. else, see if we didn't call b's __eq__ and it has it, then call and return it if the comparison is implemented,
  4. else, finally, do the comparison for identity, the same comparison as is.

We know if a comparison isn't implemented if the method returns NotImplemented.

(In Python 2, there was a __cmp__ method that was looked for, but it was deprecated and removed in Python 3.)

Let's test the first check's behavior for ourselves by letting B subclass A, which shows that the accepted answer is wrong on this count:

class A:
    value = 3
    def __eq__(self, other):
        print('A __eq__ called')
        return self.value == other.value

class B(A):
    value = 4
    def __eq__(self, other):
        print('B __eq__ called')
        return self.value == other.value

a, b = A(), B()
a == b

which only prints B __eq__ called before returning False.

Note that I also correct a small error in the question where self.value is compared to other instead of other.value - in this comparison, we get two objects (self and other), usually of the same type since we are doing no type-checking here (but they can be of different types), and we need to know if they are equal. Our measure of whether or not they are equal is to check the value attribute, which must be done on both objects.

How do we know this full algorithm?

The other answers here seem incomplete and out of date, so I'm going to update the information and show you how how you could look this up for yourself.

This is handled at the C level.

We need to look at two different bits of code here - the default __eq__ for objects of class object, and the code that looks up and calls the __eq__ method regardless of whether it uses the default __eq__ or a custom one.

Default __eq__

Looking __eq__ up in the relevant C api docs shows us that __eq__ is handled by tp_richcompare - which in the "object" type definition in cpython/Objects/typeobject.c is defined in object_richcompare for case Py_EQ:.

    case Py_EQ:
        /* Return NotImplemented instead of False, so if two
           objects are compared, both get a chance at the
           comparison.  See issue #1393. */
        res = (self == other) ? Py_True : Py_NotImplemented;
        Py_INCREF(res);
        break;

So here, if self == other we return True, else we return the NotImplemented object. This is the default behavior for any subclass of object that does not implement its own __eq__ method.

How __eq__ gets called

Then we find the C API docs, the PyObject_RichCompare function, which calls do_richcompare.

Then we see that the tp_richcompare function, created for the "object" C definition is called by do_richcompare, so let's look at that a little more closely.

The first check in this function is for the conditions the objects being compared:

  • are not the same type, but
  • the second's type is a subclass of the first's type, and
  • the second's type has an __eq__ method,

then call the other's method with the arguments swapped, returning the value if implemented. If that method isn't implemented, we continue...

    if (!Py_IS_TYPE(v, Py_TYPE(w)) &&
        PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&
        (f = Py_TYPE(w)->tp_richcompare) != NULL) {
        checked_reverse_op = 1;
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);

Next we see if we can lookup the __eq__ method from the first type and call it. As long as the result is not NotImplemented, that is, it is implemented, we return it.

    if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
        res = (*f)(v, w, op);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);

Else if we didn't try the other type's method and it's there, we then try it, and if the comparison is implemented, we return it.

    if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) {
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }

Finally, we get a fallback in case it isn't implemented for either one's type.

The fallback checks for the identity of the object, that is, whether it is the same object at the same place in memory - this is the same check as for self is other:

    /* If neither object implements it, provide a sensible default
       for == and !=, but raise an exception for ordering. */
    switch (op) {
    case Py_EQ:
        res = (v == w) ? Py_True : Py_False;
        break;

Conclusion

In a comparison, we respect the subclass implementation of comparison first.

Then we attempt the comparison with the first object's implementation, then with the second's if it wasn't called.

Finally we use a test for identity for comparison for equality.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜