2

My objective is to get the difference between two lists containing objects.

I have implemented a class named Branch and overwritten its __eq__ and __ne__ methods as follows:

class Branch(object):
    def __str__(self):
        return self.name

    def __eq__(self, other):
        if isinstance(other, Branch):
            return (self.valueFrom == other.valueFrom) \
                and (self.valueTo == other.valueTo) \
                and (self.inService == other.inService)
        return NotImplemented

    def __ne__(self, other):
        result = self.__eq__(other)
        if result is NotImplemented:
            return result
        return not result

    def __init__(self, name, valueFrom, valueTo, inService=True):
        self.name = name
        self.valueFrom = valueFrom
        self.valueTo = valueTo
        self.inService = inService

My first attempt was to use the method difference from the set type. However it appears this is not possible as it uses the hash of the object and not the __eq__method as I would like to.

Following code shows the problem:

b1 = Branch("branch1", 1, 2)
b1b = Branch("equal to branch1", 1, 2)
b2 = Branch("branch2", 2, 3)
b3 = Branch("branch3", 3, 1)
b3_off = Branch("branch3 not in service", 3, 1, False)

l1 =[b1,b2,b3]
l2 =[b1b,b2,b3_off]

difference = set(l1).difference(l2)
for branch in difference:
    print branch

Output is:

>>> 
branch1
branch3

However I wish to get as output only branch3 as b1 and b1b should be treated as equal.

Is it possible to use sets to resolve this? Or should I approach the problem from a different perspective?

Cedric Zoppolo
  • 4,271
  • 6
  • 29
  • 59
  • Possible duplicate of [Get difference between two lists](http://stackoverflow.com/questions/3462143/get-difference-between-two-lists) – Random Davis Oct 07 '16 at 17:45

1 Answers1

4

You would need to implement hash, what you choose to is up to you but the following would work:

def __hash__(self):
    return hash((self.valueFrom , self.valueTo , self.inService))

All you need to implement is hash and eq:

class Branch(object):
    def __init__(self, name, valueFrom, valueTo, inService=True):
        self.name = name
        self.valueFrom = valueFrom
        self.valueTo = valueTo
        self.inService = inService

    def __eq__(self, other):
        if isinstance(other, Branch):
            return (self.valueFrom,self.valueTo,self.inService )\
                   ==(other.valueFrom, other.valueTo, other.inService)
        return NotImplemented

    def __str__(self):
        return self.name

    def __hash__(self):
        return hash((self.valueFrom, self.valueTo,self.inService))
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • 2
    Your "you could also.." should be the correct answer. The sum of `(1, 2, 1)` and `(2,1,1)` is the same, that doesn't mean they're the same objects. – rantanplan Oct 07 '16 at 18:07
  • @rantanplan, true, I did not give it a great deal of thought as I presumed the real data may be a bit different, even using `self.valueFrom + self.valueTo + self.inService` would still work though but obviously less efficient if you really had numbers as it would fall back to eq. – Padraic Cunningham Oct 07 '16 at 18:14
  • You've already got my vote, but I don't understand why you say it would work. From the code the OP posted I don't see `self.valueFrom` being the same as `self.valueTo`. If they *happen* to have the same value is... well just that, happenstance :) But maybe I'm missing something – rantanplan Oct 07 '16 at 18:18
  • As I checked it is also necessary to have the `__eq__` and `__ne__` implemented, right? How does `difference`method work for `set` when checking if two objects are equal? – Cedric Zoppolo Oct 07 '16 at 18:19
  • @rantanplan, when the hash is the same what you have used in eq will be evaluated, using `self.valueFrom + self.valueTo + self.inService` would work but the chance of collisions is obviously much higher. Using `hash((self.valueFrom, self.valueTo,self.inService))` eq is called once for b1 and b1b as the have the same hash value. You can have the same hash and not be equal in terms of what you use in eq. i,e 2+1+True and 3 +1 would both have the value 4 but when eq is called they would not be considered equal. – Padraic Cunningham Oct 07 '16 at 18:40
  • @Cedric,all you need is eq as per the edit , if you remove the hash function and `print branch.__hash__(), id(branch)/16)`, you will see that they are the same so the hash is based on the id of the object, that is why you need to implement it to take into account what you consider to be equal objects. Unless you happened to get a hash collision then eq is never called. This /16 logic applies to python2 only. – Padraic Cunningham Oct 07 '16 at 18:47