3

I'd like to compare two objects of the same type with the dunder method _eq_ for equality. Every object stores values for "word", "pronunciation", "weight", and "source" and equality is reached, when everything is the same. My solution looks like the following and works but it feels clunky and I am sure that there is a better way.

    def __eq__(self, other):
        if self.check_other(other): # checks of both objects are snstances of LexicalEntity
            return_bool = True
            if self.word != other.get_word():
                return_bool = False
            if self.weight != other.get_weight():
                return_bool = False
            if self.source != other.get_source():
                return_bool = False
            if self.pron != other.get_pron():
                return_bool = False
            return return_bool

Thanks for your help.

Scrottz
  • 39
  • 3

3 Answers3

2

For starters, dispense with getters and setters in Python. That will make your code much less clunky and more idiomatic, i.e., you don't need other.get_word(), you just need other.word, and remove your definition of get_word, it is useless. Python != Java.

So, then for something like this, a typical implementation would be:

def __eq__(self, other):
    if isinstance(other, LexicalEntity):
        these_values = self.word, self.weight, self.source, self.pron
        other_values = other.word, other.weight, other.source, other.pron
        return these_values == other_values
    return NotImplemented # important, you don't want to return None 
        

Alternatively, you might also just use one long boolean expression:

def __eq__(self, other):
    if isinstance(other, LexicalEntity):
        return (
            self.word == other.word and self.weight == other.weight
            and self.source == other.source and self.pron == other.pron
        )
    return NotImplemented
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • Tried it and it works well. I ended up solving the problem differently but I really like your second approach. It will come in handy for future tasks and I am gonna use this for sure. Thanks. – Scrottz Aug 27 '20 at 09:13
1

I think this maybe is little more readable:

    def __eq__(self, other):
        if self.check_other(other): 
            attrs = ["word", "weight", "source", "pron"]
            return all([getattr(self, attr) == getattr(other, attr) for attr for attrs])

But I guess it's a preference if we want more readable or more smart solution

galuszkak
  • 513
  • 4
  • 25
  • I was about to post the same answer... You can remove the square-brackets (`[...]`) and use it as a generator expression to avoid creating unnecessary list in memory – Tomerikoo Aug 26 '20 at 11:46
0

Getters and setters don't make much sense in Python, you should start using the @property annotation instead, if you do have important validations - if you're just doing this for data encapsulation, Python principles are much more loose in that aspect, so just ditch getters/setters.

As for asserting equality, if you want to avoid manually referring to each attribute, the below reflection is appliable to virtually any case:

def __eq__(self, other):
    if isinstance(other, self.__class__):
        attrs = [
            a for a in dir(self) if not a.startswith('_') and not callable(getattr(self, a))
        ]
        return all([getattr(self, attr) == getattr(other, attr) for attr in attrs])
    
    return NotImplemented

As @juanpa.arrivillaga already mentioned, returning the NotImplemented (not the same as raising NotImplementedError, as noted in the comments below) is important because if other is from a different class this stops you from returning None in the equality check. A better explanation of why return NotImplemented is the fallback in these cases is found in this answer.

Julio Cezar Silva
  • 2,148
  • 1
  • 21
  • 30
  • 1
    You're not supposed to `raise NotImplementedError`. You're supposed to `return NotImplemented`. [`NotImplementedError`](https://docs.python.org/3/library/exceptions.html#NotImplementedError) is for stuff like abstract methods, or methods you need to implement but just haven't fully written yet. [`NotImplemented`](https://docs.python.org/3/library/constants.html#NotImplemented) is a special return value for binary special methods to indicate they don't support the other operand. – user2357112 Aug 26 '20 at 23:18
  • Thank you, @user2357112supportsMonica, I've corrected the answer. For anyone looking for a better explanation of this, try [this answer](https://stackoverflow.com/a/879005/8272600). – Julio Cezar Silva Aug 27 '20 at 05:28