Home > Enterprise >  python3 undefined __ne__ returns same value as defined __eq__
python3 undefined __ne__ returns same value as defined __eq__

Time:04-05

I have a class MyClass where __eq__ is defined as follows:

def __eq__(self, other):
        try:
            other = MyClass(other)
        except Exception as e:
            raise ValueError(f"MyClass.__eq__ failed: {other} doesn't appear to be a valid MyClass type. \n", e )

        prefix_i1 = re.findall("[ab]" , self       )
        prefix_i2 = re.findall("[ab]" , other )
        if len( set( prefix_i1   prefix_i2 ) ) > 1:
            return False    # ie: "a07" != "b07" BUT  "k07" == "b07"
        else:
            return self.num_string == other.num_string   # eg: "i3" == "i03"

where

self.num_string = "".join(filter(str.isdigit, self               )).zfill(2)

Unexpectedly, I get this result:

> MyClass('b05') == MyClass('i05')
True
> MyClass('b05') != MyClass('i05')
True

I thought __ne__ was by default not __eq__ and defining it explicitly was discouraged since Python 3.

What am I doing wrong?

Here is a minimal reproducible example:

import re

class MyClass(str):
    def __new__(cls, *args, **kwargs):
        if  args[0] is None:
            return str.__new__(cls, '' )
        if not ( isinstance(args[0], int) or 
                 ( isinstance(args[0], str) and re.match("[iab]?[0-9]{1,2}", args[0].lower()) ) ):
            raise ValueError("MyClass failed because the provided input '{}' is not valid... .".format(args[0])) 
        lower_str_input = str(args[0]).lower()
        return str.__new__(cls, lower_str_input ) 
    def __init__(self, input_string=''):
      self.num_string = "".join(filter(str.isdigit, self               )).zfill(2)
    def __eq__(self, other):
        try:
            other = MyClass(other)
        except Exception as e:
            raise ValueError("MyClass.__eq__ failed" )
        prefix_i1 = re.findall("[ab]" , self       )
        prefix_i2 = re.findall("[ab]" , other )
        if len( set( prefix_i1   prefix_i2 ) ) > 1:
            return False    # ie: "a07" != "b07" BUT  "k07" == "b07"
        else:
            return self.num_string == other.num_string   # eg: "i3" == "i03"

MyClass('b05') == MyClass('i05')
MyClass('b05') != MyClass('i05')

CodePudding user response:

I thought __ne__ was by default not __eq__ and defining it explicitly was discouraged since Python 3.

For your own classes, yes. They will derive from object by default, which implements that logic.

However, built-ins (and thus everything derived from them) can be a bit strange.

Here is a simpler example:

... class x(str):
...     def __eq__(self, other):
...         print('eq called')
...         return str.__eq__(self, other)
... 
>>> x('foo') != 'foo'
False

The answer is correct, but __eq__ is not called (thus there is no message printed). This answer comes from str.__ne__, which (apparently) is separately implemented (not in terms of __eq__).

  • Related