1

For below defined user defined python class, a == b is False

>>> class Account():
        def __init__(self, account_holder):
            self.balance = 0
            self.holder = account_holder

>>> a = Account('Jim')
>>> b = Account('Jim')
>>> a is b
False
>>> a == b
False

But in below cases, equality(==) operator shows True

>>> lst1 = [1, 2]
>>> lst2 = [1, 2]
>>> lst1 == lst2
True            # this is true
>>> lst1 is lst2
False
>>> str1 = 'abc'
>>> str2 = 'abc'
>>> str1 == str2
True            # this is true
>>> str1 is str2
True
>>> tup1 = (1, 2)
>>> tup2 = (1, 2)
>>> tup1 == tup2
True             # this is true
>>> tup1 is tup2
False
  1. How do I understand the working of equality operator(==), when user defined classes are defined in python?

  2. Which method of class object provide identity to all instances of any user defined class in python?

poke
  • 369,085
  • 72
  • 557
  • 602
overexchange
  • 15,768
  • 30
  • 152
  • 347

2 Answers2

2

You will have to implement class equality yourself by overriding __eq__ method in your class. See here for more details: __eq__

In your particular case, something like this:

class Account():
  def __init__(self, account_holder):
    self.balance = 0
    self.holder = account_holder

  def __eq__(self, other):
     return self.holder == other.holder

  def __ne__(self, other):
     return not self.__eq__(other)

Now a == b should return True.

If you want more examples, How to override comparison operators, provides good ones.

Edit: As mentioned by @SergeBallesta in the comments and as urged by documentation too, it is a good idea to override the reflection of the __eq__() method which is __ne__().

Sandman
  • 5,432
  • 5
  • 20
  • 23
  • For identity, what do I need to override? – overexchange May 28 '15 at 05:46
  • You can't override identity. It wouldn't make any sense. Two objects are either the same object, or not. Pretending that two different objects are the same would be mad. – kindall May 28 '15 at 05:55
  • @kindall Firstly `object.__hash__()` actually returns identity for all instances of user defined classes, correct? If yes, then what is the return type/typesizebits of `object.__hash__()`? In java, it is must to overide identity because `public int hashCode()` returns 32 bit integer, despite I create more than 2^32 objects. – overexchange May 28 '15 at 05:59
  • @overexchange __hash__() returns an integer. It is just a design choice that unless overridden the __hash__() function by default uses a result derived from id(obj) and id(obj) in Cpython is the address of the object. The "is" operator on the other hand tests whether the it is exact same location in memory or not, to put it very simply. Check this SO question that explains this excellently: http://stackoverflow.com/questions/132988/is-there-a-difference-between-and-is-in-python – Sandman May 28 '15 at 06:03
  • If you override `__eq__`, you should also override `__ne__` ... – Serge Ballesta May 28 '15 at 06:04
  • @Sandman Can I create more than 2^32 instances of a user defined class in python using 64 bit python interpreter? If yes, does `__hash__()` return 32 bit integer or 64 bit integer? – overexchange May 28 '15 at 06:08
  • 1
    Integers in Python can be any number of bits. Regardless, it's perfectly legal for two different objects to have the same hash. – kindall May 28 '15 at 06:10
  • @kindall legal? But if I need to place an object in 'value' part of dictionary where I want the key part of dictionary to be an integer(address of object), like hashtable. This is why, in java overriding `hashCode()` and `equal()` is mandatory and this overiding is nothing specfic to language – overexchange May 28 '15 at 06:24
  • @overexchange Stop thinking in pointers or integers. You do get a reference to an object; not some magic integer that happens to be the hash. The hash value of an object is just *a hint*. It doesn’t decide over identity and isn’t used to refer to an object. – poke May 28 '15 at 06:26
  • @poke But this is what python doc says: `if it defines __cmp__() or __eq__() but not __hash__(), its instances will not be usable in hashed collections.` What do you mean stop thinking? Where are pointers here? – overexchange May 28 '15 at 06:33
  • @overexchange Objects needs to be hashable for them to be placed in a hashed collection, but that does not mean that the hash has any effect on identity. In face, you can create a type for which all objects return a constant hash (let’s say `1`), and you can still put any amount of them inside a set, or use them as keys for a dictionary. The hash is used as a hint, but the identity of two objects is determined by the actual memory identity which you cannot change. – poke May 28 '15 at 06:42
  • @poke So, actual memory identity value can be retrieved using `id()` where as `__hash__()` is just an attribute of an instance of class which generates hashkey(if required). Is that correct? – overexchange May 28 '15 at 06:48
  • @overexchange Yes, that sounds about right. – poke May 28 '15 at 06:56
  • @poke How do I understand this from python doc? `x.__hash__() returns a result derived from id(x)` – overexchange May 28 '15 at 09:25
  • @overexchange That’s just referring to the default value. – poke May 28 '15 at 09:44
  • @poke So, In python, by default, hash value and actual memory identity value of an object are same. Is that correct? – overexchange May 28 '15 at 09:46
  • No, they are not the same. As the docs say: “**User-defined classes** […] have __hash__() methods by default; […] and x.__hash__() returns a result **derived** from id(x).” (emphasis mine) – poke May 28 '15 at 10:26
  • Sorry to butt in so late, but @overexchange, in Cython, the default value of id (x) is the memory address of the object according to the documentation. Now there may be some other transformation being applied before __hash__() returns this as the hash value. Does that make sense? I hope this doesn't confuse you anymore. – Sandman May 28 '15 at 10:39
  • @Sandman transformation being applied before `__hash__()` returns? I did not get this statement. What is that transformation? – overexchange May 28 '15 at 15:20
  • @overexchange That is implementation specific and immatertial. However, in very simple terms think of the default __hash__() being implemented as follows: def __hash__(): m = id(self); return do_some_transformation(m) – Sandman May 28 '15 at 21:20
1

override __eq__ and __ne__ methods.

class Account():
    def __init__(self, account_holder):
        self.balance = 0
        self.holder = account_holder
    def __eq__(self, other):
        """Override the default equals"""
        return (isinstance(other, self.__class__)
            and self.__dict__ == other.__dict__)
    def __ne__(self, other):
        """non-equality"""
        return not self.__eq__(other)

a = Account('Jim')
b = Account('Jim')

print a == b

c = Account('Not Jim')
print a == c

Output:

True
False

About identity is operator. a is b will be True if a and b both hold the reference to the same object.

a = b
print a is b # return True
print a is c # return False

You can read about similar functions here.

Michael Kazarian
  • 4,376
  • 1
  • 21
  • 25