3

I am using python 3.6. to create two lists. I want to filter it if all of the instances attribute values are the same.

I wrote this code, but it returns false. How to filter it?

class MyCls:
    def __init__(self, **kwargs):
        self.x = kwargs.get('x')
        self.y = kwargs.get('y')
        self.z = kwargs.get('z')

    def __str__(self):
        return str(self.__dict__)

    def __hash__(self):
        return hash(str(self.__dict__))

    def __eq__(self, other):
        return str(self.__hash__) == str(other.__hash__)


a = MyCls(x='a', y='b', z='c')
b = MyCls(x='a', y='b', z='c')

ab = [a, b]

print(a is b)
# False
print(a == b)
# False

s = set(ab)
print(s)
# print(2 instances)
1
  • the hash of an object should be constant for it's whole lifetime - else some things will break in some unexpected ways (cf docs.python.org/3/reference/datamodel.html#object.__hash__: "If a class defines mutable objects and implements an __eq__() method, it should not implement __hash__(), since the implementation of hashable collections requires that a key’s hash value is immutable (if the object’s hash value changes, it will be in the wrong hash bucket).") Commented Nov 6, 2018 at 11:15

1 Answer 1

4

I'm answering on the premise that you are playing around with __hash__ and __eq__ for educational purposes.

I find your hashing and equality methods... weird.

Anyway, the concrete problem with your code is that __eq__ does not call the __hash__ method.

You can use

return str(self.__hash__()) == str(other.__hash__())

in __eq__ - or better (but still weird)

return hash(self) == hash(other)

With this adjustment you get

>>> hash(a)
>>> 8280333490847949293
>>> hash(b)
>>> 8280333490847949293
>>> a == b
>>> True

However, a fundamental flaw with this design is that an instance of MyClass will compare equal with any other object that hashes the same by accident.

>>> class C:
...:    def __hash__(self):
...:        return 8280333490847949293
...:    
>>> C() == a
>>> True

If you want to compare instance attributes, why not compare the instance dicts directly?

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks I understood my mistake. but how to compare instances dicts directly?
@rootpetit you can access an instance dict with my_object.__dict__ or vars(my_object) and compare dictionaries with ==. Remember that instances of two different classes can still have equal __dict__ attributes, so you probably also need an isinstance check.
Thanks a lot! I have to fix __eq__ to check isinstance of MyCls and return self.__dict__ == other.__dict__ to compare attribute (and remove hash).
@rootpetit you're welcome! If you want to put your objects into a set, they need to be hashable. Have a look at this question.
I think usecase, and If I use it with set, I implement above class hash. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.