Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'Reversed' comparison operator in Python

class Inner():

    def __init__(self, x):
        self.x = x

    def __eq__(self, other):
        if isinstance(other, Inner):
            return self.x == other.x
        else:
            raise TypeError("Incorrect type to compare")

class Outer():

    def __init__(self, y):
        self.y = Inner(y)

    def __eq__(self, other):
        if isinstance(other, Outer):
            return self.y == other.y
        elif isinstance(other, Inner):
            return self.y == other
        else:
            raise TypeError("Incorrect type to compare")


if __name__ == "__main__":

    a = Outer(1)
    b = Inner(1)

    print(a == b) # ok no problem
    print(b == a) # This will raise a type error

In the example I have inner and outer class. I have no control over what Inner implements just wanted to simulate the situation. I have only control over Outer's behavior. I want Outer instances to be able to compare to Inner instances (not just equality). With the given implementation only the first comparison works because that is calling Outer's __eq__ method allowed to be compared to Outer and Inner instances but the second one is calling Inner's __eq__ which will not allow the comparison to Outer - heck it doesn't know Outer exists why should it bother to implement it. Is there a way to get the second type of comparison to work, with something similar like the __radd__ and such functions. I know for instance in C++ you resolve this with inline operator definitions, but we don't have such in Python.

like image 300
blint587 Avatar asked Jul 18 '16 10:07

blint587


1 Answers

Not to put too fine a point on it: Inner.__eq__ is broken. At the very least, rather than throwing an error it should return NotImplemented, which would allow Python to try the reverse comparison:

When NotImplemented is returned, the interpreter will then try the reflected operation on the other type, or some other fallback, depending on the operator. If all attempted operations return NotImplemented, the interpreter will raise an appropriate exception.

Better yet it would use "duck typing", rather than insisting on a specific class (unless the class, rather than its interface, is an explicitly important part of the comparison):

def __eq__(self, other):
    try:
        return self.x == other.x
    except AttributeError:
        return NotImplemented

However, as you say you cannot control this, you will have to manually implement similar functionality, for example:

def compare(a, b):
    """'Safe' comparison between two objects."""
    try:
        return a == b
    except TypeError:
        return b == a

as there is no such thing as __req__ in Python's data model.

like image 155
jonrsharpe Avatar answered Nov 04 '22 12:11

jonrsharpe