Skip to content
Advertisement

Why doesn’t Python have a “__req__” (reflected equality) method?

I have a little helper class:

class AnyOf(object):
    def __init__(self, *args):
        self.elements = args
    def __eq__(self, other):
        return other in self.elements

This lets me do sweet magic like:

>>> arr = np.array([1,2,3,4,5])
>>> arr == AnyOf(2,3)
np.array([False, True, True, False, False])

without having to use a list comprehension (as in np.array(x in (2,3) for x in arr).

(I maintain a UI that lets (trusted) users type in arbitrary code, and a == AnyOf(1,2,3) is a lot more palatable than a list comprehension to the non-technically savvy user.)

However!

This only works one way! For example, if I were to do AnyOf(2,3) == arr then my AnyOf class’s __eq__ method never gets called: instead, the NumPy array’s __eq__ method gets called, which internally (I would presume) calls the __eq__ method of all its elements.

This lead me to wonder: why does Python not allow a right-sided equivalent to __eq__? (Roughly equivalent to methods like __radd__, __rmul__, et cetera.)

Advertisement

Answer

An __req__ may have been considered more confusing than useful in the language. Consider if class Left defines __eq__ and class Right defines __req__, then Python is obliged to make a consistent decision about who gets called first in Left() == Right() (and we would presumably like the result to be equivalent, either way). They can’t both win.

However, the Python datamodel does allow a way for you to do what you want here. From either side you can control this comparison, but you’ll need to define AnyOf in a particular way. If you want AnyOf to control the eq from the right hand side, you may define it to be a subclass of np.ndarray.

if I were to do AnyOf(2,3) == arr then my AnyOf class’s __eq__ method never gets called

Actually, no, there’s a fundamental misunderstanding evident here. The left hand side always gets first try at the equality comparison, unless the right hand side is a subclass of the type of the left hand side.

arr == AnyOf(2,3)

In the comparison shown above, your custom __eq__ is being called, because the numpy array calls it! So np.ndarray wins, and it decides to check once per element. It literally could do anything else, including not calling your AnyOf.__eq__ at all.

AnyOf(2,3) == arr

In the comparison shown above, your class does get the first try at the comparison, and it fails because of the way in was used – return other in self.elements is checking if an array is in a tuple.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement