- Equality Testing (__eq__)
The problem:
class A(object):
def __init__(self, x):
self.x = x
a1 = A(5)
a2 = A(5)
print(a1 == a2) # False!
The partial solution: __eq__
class A(object):
def __init__(self, x):
self.x = x
def __eq__(self, other):
return (self.x == other.x)
a1 = A(5)
a2 = A(5)
print(a1 == a2) # True
print(a1 == 99) # crash (darn!)
A better solution:
class A(object):
def __init__(self, x):
self.x = x
def __eq__(self, other):
return (isinstance(other, A) and (self.x == other.x))
a1 = A(5)
a2 = A(5)
print(a1 == a2) # True
print(a1 == 99) # False (huzzah!)
- Converting to Strings (__str__ and __repr__)
The problem:
class A(object):
def __init__(self, x):
self.x = x
a = A(5)
print(a) # prints <__main__.A object at 0x102916128> (yuck!)
The partial solution: __str__
class A(object):
def __init__(self, x):
self.x = x
def __str__(self):
return "A(x=%d)" % self.x
a = A(5)
print(a) # prints A(x=5) (better)
print([a]) # prints [<__main__.A object at 0x102136278>] (yuck!)
The better solution: __repr__
# Note: repr should be a computer-readable form so that
# (eval(repr(obj)) == obj), but we are not using it that way.
# So this is a simplified use of repr.
class A(object):
def __init__(self, x):
self.x = x
def __repr__(self):
return "A(x=%d)" % self.x
a = A(5)
print(a) # prints A(x=5) (better)
print([a]) # [A(x=5)]
- Using in Sets and Dictionaries (__hash__ and __eq__)
The problem:
class A(object):
def __init__(self, x):
self.x = x
s = set()
s.add(A(5))
print(A(5) in s) # False
d = dict()
d[A(5)] = 42
print(d[A(5)]) # crashes
The solution: __hash__ and __eq__
class A(object):
def __init__(self, x):
self.x = x
def __hash__(self):
return hash(self.x)
def __eq__(self, other):
return (isinstance(other, A) and (self.x == other.x))
s = set()
s.add(A(5))
print(A(5) in s) # True (whew!)
d = dict()
d[A(5)] = 42
print(d[A(5)]) # works!
A better (more generalizable) solution
# Your getHashables method should return the values upon which
# your hash method depends, that is, the values that your __eq__
# method requires to test for equality.
# CAVEAT: a proper hash function should only test values that will not change!
class A(object):
def __init__(self, x):
self.x = x
def getHashables(self):
return (self.x, ) # return a tuple of hashables
def __hash__(self):
return hash(self.getHashables())
def __eq__(self, other):
return (isinstance(other, A) and (self.x == other.x))
s = set()
s.add(A(5))
print(A(5) in s) # True (still works!)
d = dict()
d[A(5)] = 42
print(d[A(5)]) # works!
- Fraction Example
# Very simple, far-from-fully implemented Fraction class
# to demonstrate the OOP ideas from above.
# Note that Python actually has a full Fraction class that
# you would use instead (from fractions import Fraction),
# so this is purely for demonstrational purposes.
def gcd(x, y):
if (y == 0): return x
else: return gcd(y, x%y)
class Fraction(object):
def __init__(self, num, den):
# Partial implementation -- does not deal with 0 or negatives, etc
g = gcd(num, den)
self.num = num // g
self.den = den // g
def __repr__(self):
return '%d/%d' % (self.num, self.den)
def __eq__(self, other):
return (isinstance(other, Fraction) and
((self.num == other.num) and (self.den == other.den)))
def times(self, other):
if (isinstance(other, int)):
return Fraction(self.num * other, self.den)
else:
return Fraction(self.num * other.num, self.den * other.den)
def __hash__(self):
return hash((self.num, self.den))
def testFractionClass():
print('Testing Fraction class...', end='')
assert(str(Fraction(2, 3)) == '2/3')
assert(str([Fraction(2, 3)]) == '[2/3]')
assert(Fraction(2,3) == Fraction(2,3))
assert(Fraction(2,3) != Fraction(2,5))
assert(Fraction(2,3) != "Don't crash here!")
assert(Fraction(2,3).times(Fraction(3,4)) == Fraction(1,2))
assert(Fraction(2,3).times(5) == Fraction(10,3))
s = set()
assert(Fraction(1, 2) not in s)
s.add(Fraction(1, 2))
assert(Fraction(1, 2) in s)
s.remove(Fraction(1, 2))
assert(Fraction(1, 2) not in s)
print('Passed.')
if (__name__ == '__main__'):
testFractionClass()