Shamshad Alam
Shamshad Alam

Reputation: 1874

Unusual behavior of set equality in python

When I am running this line of code, I could not understand why both lists are equal but sets created from the same list are not

class Field(object):
    def __init__(self, fieldnames):
        self.name = fieldnames[0]
        self.alias = frozenset(fieldnames)

    def __eq__(self, other):
        if not isinstance(other, Field):
            return False
        return len(self.alias & other.alias) >= 1

    def __hash__(self):
        return hash(self.name)

    def __str__(self):
        return "{field_name: %s}" % self.name

    def __repr__(self):
        return "<Field: (%s: %r)" %(self.name, self.alias)

In [185]: field_list1 = [["Field 1"], ["Field 2"], ["Field 3"]]

In [186]: field_list2 = [["Field 1"], ["Field 21", "Field 2"], ["Field 3"]]

In [187]: field1 = [Field(f) for f in field_list1]

In [188]: field2 = [Field(f) for f in field_list2]

In [189]: field1 == field2
Out[189]: True

In [190]: set(field1) == set(field2)
Out[190]: False

According to python docs on set two sets are equal if each element of first set is in the second set and vice versa. As per that definition both set should be equal, but I am not sure why they are not.

Therefore I wanted to know reason behind such behavior?

Upvotes: 1

Views: 74

Answers (2)

Daniel Roseman
Daniel Roseman

Reputation: 599628

You have different comparisons going on here.

When comparing two lists, each element in the first list is compared with the equivalent element in the other list via ==, which calls your eq method. Although your code for that method is very odd (why not just len(self.alias) == len(other.alias)?), it results in a True or False depending on the relative sizes of the alias attributes.

However sets work completely differently. There comparison is done by hash, not by equality; and you have defined your __hash__ method to return different result based on the name, not the alias.

Upvotes: 3

MB-F
MB-F

Reputation: 23637

field1 == field2 performs item-wise comparison using == oprerator (by calling __eq__).

set(field1) == set(field2) checks if all elements are in both sets. Elements in sets are identified by their hashes. You calculate the hash from the names. Some elements in the list have different names, so they are different set elements.

print(field1[1].name)  # 'Field 2'
print(field2[1].name)  # 'Field 21'

In summary, list comparison is based on __eq__ but set comparison is based on __hash__. They are based on totally different calculations in the Field class, so you get different results.

Upvotes: 1

Related Questions