I am a novice, so please excuse non-standard terminology and let me know if I should add code to make this question more clear.
Let's say that we try to make a class "Rational" in Python. (I know that one is already built in, but ignore that for the purpose of this question.)
We can use __add__
and __mul__
, for example, to teach Python how to interpret code of the form a + b
or a * b
,
where a
and b
are Rationals.
Now, it may happen that, somewhere else, one wants to compute a + b
, where a
is a rational but b
is an integer. This we can do by modifying our __add__
code within the Rational class to include an if statement, for instance,
def __add__(self, b):
if isinstance(b, int):
brat = rational(b, 1)
return self + brat
else:
y = rational(self.num*b.den + b.num*self.den , b.den*self.den)
y = y.lowest_terms()
return y
We can similarly modify our __mul__
code, our __div__
code, etc. But there are at least two problems with this kind of solution:
int
. The first
argument still has to be a Rational; there's no way to write a
method in the Rational class that allows us to add a + b
where a is
an int and be is a Rational.Does such a technique exist? (I have tagged this coercion because I think this is what's called coercion in other contexts, but my understanding is that coercion is deprecated in Python.)
You can avoid the repetition by doing the mapping in the class's initializer. Here's a simple demo that handles integers. Handling float
s properly will be left as an exercise for the reader. :) However, I have shown how to easily implement __radd__
, and __iadd__
, which is the magic method (aka dunder method) that handles +=
.
My code retains rational
from your code as the class name, even though class names in Python are conventionally CamelCase.
def gcd(a, b):
while b > 0:
a, b = b, a%b
return a
class rational(object):
def __init__(self, num, den=1):
if isinstance(num, rational):
self.copy(num)
else:
self.num = num
self.den = den
def copy(self, other):
self.num = other.num
self.den = other.den
def __str__(self):
return '{0} / {1}'.format(self.num, self.den)
def lowest_terms(self):
g = gcd(self.num, self.den)
return rational(self.num // g, self.den // g)
def __add__(self, other):
other = rational(other)
y = rational(self.num*other.den + other.num*self.den, other.den*self.den)
return y.lowest_terms()
def __radd__(self, other):
return rational(other) + self
def __iadd__(self, other):
self.copy(self + rational(other))
return self
a = rational(1, 4)
b = rational(2, 5)
c = a + b
print a
print b
print c
print c + 5
print 10 + c
c += 10
print c
output
1 / 4
2 / 5
13 / 20
113 / 20
213 / 20
213 / 20
You may like to reserve that copy
method for internal use; the usual convention is to prepend such names with a single underscore.