Skip to content

Exercises: Series III

Rational numbers (with correction)

A rational is a number defined by a numerator and a denominator. The denominator should not be zero.

Step1

A simple class for rationals (=fractions).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python3

class Rational(object):

  def __init__(self, num, denom):
    self._n = num
    self._d = denom

  def __repr__(self):
    return "%d/%d" % (self._n, self._d)

R1 = Rational(1,2)
R2 = Rational(1,4)
R3 = Rational(5,7)

print("R1 = ", R1)
print("R2 = ", R2)
print("R3 = ", R3)

Step2

Let's add some accessors for the numerator and the denominator

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/usr/bin/env python3

class Rational(object):

  def __init__(self, num, denom):
    self.put(num, denom)

  def __repr__(self):
    return "%d/%d" % (self._n, self._d)

  def getNum(self):
    return self._n

  def putNum(self, num):
    self._n = num

  def getDenom(self):
    return self._d

  def putDenom(self, denom):
    self._d = denom

  def get(self):
    return self._n, self._d

  def put(self, num, denom):
    self.putNum(num)
    self.putDenom(denom)


R1 = Rational(1,2)
R2 = Rational(1,4)
R3 = Rational(5,7)

print("R1 = ", R1)
print("R2 = ", R2)
print("R3 = ", R3)

R3.put(-7,13)
print("R3 = ", R3)
print("R1.num = ", R1.getNum())
print("R1.denom = ", R1.getDenom())
R1.putNum(5)
R2.putDenom(3)
print("R1 = ", R1)
print("R2 = ", R2)
print("R3 = ", R3)

Step 3

Hide all accessors in properties.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env python3

class Rational(object):
  __slots__  = ["__d", "__n"]

  def __init__(self, num, denom):
    self.frac = num, denom

  def __repr__(self):
    return "%d/%d" % (self.__n, self.__d)

  def __getNum(self):
    return self.__n

  def __putNum(self, num):
    self.__n = num

  def __getDenom(self):
    return self.__d

  def __putDenom(self, denom):
    if (denom == 0):
      raise ValueError("Denominator should not be 0")
    self.__d = denom

  def __put(self, t):
    self.num = t[0]
    self.denom = t[1]
  def __get(self):
    return self.__n, self.__d

  num = property(__getNum, __putNum)
  denom = property(__getDenom, __putDenom)
  frac = property(__get, __put)

R1 = Rational(1,2)
R2 = Rational(1,4)
R3 = Rational(5,7)

print("R1 = ", R1)
print("R2 = ", R2)
print("R3 = ", R3)

R3.frac = -7, 13
print("R3 = ", R3)
print("R1.num = ", R1.num)
print("R1.denom = ", R1.denom)
R1.num = 5
R2.denom = 3
print("R1 = ", R1)
print("R2 = ", R2)
print("R3 = ", R3)

Step 4

Add the multiplication operation. This multiplication can be defined as a object method or a class (static) method (the difference between static and class methods in Python is beyond of this scope.).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/usr/bin/env python3

class Rational(object):
  __slots__  = ["__d", "__n"]

  def __init__(self, num, denom):
    self.frac = num, denom

  def __repr__(self):
    return "%d/%d" % (self.__n, self.__d)

  def __getNum(self):
    return self.__n

  def __putNum(self, num):
    self.__n = num

  def __getDenom(self):
    return self.__d

  def __putDenom(self, denom):
    if (denom == 0):
      raise ValueError("Denominator should not be 0")
    self.__d = denom

  def __put(self, t):
    self.num = t[0]
    self.denom = t[1]
  def __get(self):
    return self.__n, self.__d

  num = property(__getNum, __putNum)
  denom = property(__getDenom, __putDenom)
  frac = property(__get, __put)

  def mult(R1, R2):
    n = R1.num*R2.num
    d = R1.denom*R2.denom
    return Rational(n, d)
  mult = staticmethod(mult)


  def __mul__(self, other):
    n = self.num*other.num
    d = self.denom*other.denom
    return Rational(n, d)



R1 = Rational(1,2)
R2 = Rational(1,4)
R3 = Rational(5,7)

print("R1 = ", R1)
print("R2 = ", R2)
print("R3 = ", R3)

R3.frac = -7, 13
print("R3 = ", R3)
print("R1.num = ", R1.num)
print("R1.denom = ", R1.denom)
R1.num = 5
R2.denom = 3
print("R1 = ", R1)
print("R2 = ", R2)
print("R3 = ", R3)
print("R1*R2 = ", Rational.mult(R1, R2))
print("R1*R2 = ", R1*R2)

Step 5

Add documentation, clean the code so you can use it as a module.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/usr/bin/env python3

class Rational(object):
  """Rational class

A Rational is defined with a numerator (num) and
a denominator (denom). The denom attribute should not
be zero.
  """
  __slots__  = ["_d", "_n"]

  def __init__(self, num = 0, denom = 1):
    """Construtor. By default, a rational object is 0/1"""
    self.frac = num, denom

  def __repr__(self):
    """Representation of a fraction in the form "n/d"."""
    return "%d/%d" % (self._n, self._d)

  def _getNum(self):
    return self._n

  def _putNum(self, num):
    self._n = num

  def _getDenom(self):
    return self._d

  def _putDenom(self, denom):
    if (denom == 0):
      raise ValueError("Denominator should not be 0")
    self._d = denom

  def _put(self, t):
    self.num = t[0]
    self.denom = t[1]
  def _get(self):
    return self._n, self._d

  _num_doc = """Rational attribute corresponding to the numerator"""
  _dem_doc = """Rational attribute corresponding to the denominator"""
  _frc_doc = """Tuple (n, d) representing the rational numerator and denominator"""
  num = property(_getNum, _putNum, doc = _num_doc)
  denom = property(_getDenom, _putDenom, doc = _dem_doc)
  frac = property(_get, _put, doc = _frc_doc)

  def mult(R1, R2):
    """static method to multiply two rational numbers"""
    n = R1.num*R2.num
    d = R1.denom*R2.denom
    return Rational(n, d)
  mult = staticmethod(mult)


  def __mul__(self, other):
    """multiplication of a rational number by another"""
    n = self.num*other.num
    d = self.denom*other.denom
    return Rational(n, d)

if __name__ == "__main__":
  R1 = Rational(1,2)
  R2 = Rational(1,4)
  R3 = Rational(5,7)

  print("R1 = ", R1)
  print("R2 = ", R2)
  print("R3 = ", R3)

  R3.frac = -7, 13
  print("R3 = ", R3)
  print("R1.num = ", R1.num)
  print("R1.denom = ", R1.denom)
  R1.num = 5
  R2.denom = 3
  print("R1 = ", R1)
  print("R2 = ", R2)
  print("R3 = ", R3)
  print("R1*R2 = ", Rational.mult(R1, R2))
  print("R1*R2 = ", R1*R2)
pydoc Rational
Help on module Rational:

NAME
    Rational - # -*- coding: UTF-8 -*-

FILE
    Rational.py

CLASSES
    __builtin__.object
        Rational

    class Rational(__builtin__.object)
     |  Rational class
     |
     |  A Rational is defined with a numerator (num) and
     |  a denominator (denom). The denom attribute should not
     |  be zero.
     |
     |  Methods defined here:
     |
     |  __init__(self, num=0, denom=1)
     |      Construtor. By default, a rational object is 0/1
     |
     |  __mul__(self, other)
     |      multiplication of a rational number by another
     |
     |  __repr__(self)
     |      Representation of a fraction in the form "n/d".
     |
     |  ----------------------------------------------------------------------
     |  Static methods defined here:
     |
     |  mult(R1, R2)
     |      static method to multiply two rational numbers
     |
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |
     |  denom
     |      Rational attribute corresponding to the denominator
     |
     |  frac
     |      Tuple (n, d) representing the rational numerator and denominator
     |
     |  num
     |      Rational attribute corresponding to the numerator

Using the ''Rational'' module:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/usr/bin/env python3

import Rational

R1 = Rational.Rational(1, 2)
R2 = Rational.Rational()
R2.frac = 2, 1
print("R1 = ", R1)
print("R2 = ", R2)
print("R1*R2 = %s" % (R1*R2))

Polar points

A polar point on a plane can be defined a distance from the origin and an angle (in degree).

Step 1

Write a simple ''Polar'' class.

Skeleton:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3

import math # to convert degrees in radians

class Polar(object): 
  def __init__(self, r, degree):
    pass # ADD YOUR CODE HERE

  def putPolar(self, r, degree):
    pass # ADD YOUR CODE HERE

  def __repr__(self):
    pass # ADD YOUR CODE HERE

P1 = Polar(1.0, 180.0)
P2 = Polar(3.0, 90.0)

print("P1 = ", P1)
print("P2 = ", P2)

Solution

Step 2

Add accessors

import math

class Polar(object):
  def __init__(self, r, degree):
    """Polar constructor"""
    pass

  def putPolar(self, r, degree):
    """Polar accessor to modify the attributes of a Polar object (mutator)"""
    pass

  def __repr__(self):
    """Representation of a Polar object"""
    pass

  def getPolar(self):
    """Polar accessor that returns the attributes of a Polar object as a polar tuple (r, theta)"""
    pass

  def getCart(self):
    """Polar accessor that returns the attributes of a Polar object as a cartesian tuple (x, y)"""
    pass

P1 = Polar(1.0, 180.0)
P2 = Polar(2.0, 90.0)

print("P1 = ", P1)
print("P2 = ", P2)

R2, T2 = P2.getPolar()
X2, Y2 = P2.getCart()

print(R2, T2)
print(X2, Y2)

Expected result:

P1 =  r = 1.000000, theta = 3.141593
P2 =  r = 2.000000, theta = 1.570796
2.0 90.0
1.22464679915e-16 2.0

Solution

Step 3

class Polar(object):
  def __init__(self, r, degree):
    """Polar constructor"""
    pass

  def putPolar(self, r, degree):
    """Polar accessor to modify the attributes of a Polar object (mutator)"""
    pass

  def __repr__(self):
    """Representation of a Polar object"""
    pass

  def getPolar(self):
    """Polar accessor that returns the attributes of a Polar object as a polar tuple (r, theta)"""
    pass

  def getCart(self):
    """Polar accessor that returns the attributes of a Polar object as a cartesian tuple (x, y)"""
    pass

  def putCart(self, x, y):
    """Polar accessor that modify the attributes of a Polar object by giving the corresponding cartesian coordinates"""
    pass

P1 = Polar(1.0, 180.0)
P2 = Polar(2.0, 90.0)

print("P1 = ", P1)
print("P2 = ", P2)

R2, T2 = P2.getPolar()
X2, Y2 = P2.getCart()

print(R2, T2)
print(X2, Y2)

P1.putCart(0.0, -3.0)
P2.putCart(4.0, 0.0)

print("P1 = ", P1)
print("P2 = ", P2)

Expected result:

P1 =  r = 1.000000, theta = 3.141593
P2 =  r = 2.000000, theta = 1.570796
2.0 90.0
1.22464679915e-16 2.0
P1 =  r = 3.000000, theta = -1.570796
P2 =  r = 4.000000, theta = 0.000000

Solution

Step 4

Add properties

class Polar(object):
  """The Polar Class that describes point2D object either in polar coordinates (r, theta), with theta in degrees, or in cartesian coordinates (x, y)"""

  def __init__(self, r, degree):
    """Polar constructor"""
    pass

  def __repr__(self):
    """Representation of a Polar object"""
    pass

  def __putPolar(self, (r, degree)):
    pass

  def __getPolar(self):
    pass

  def __getCart(self):
    pass

  def __putCart(self, (x, y) ):
    pass

  __cart_doc = """Cartesian attributes of a Polar object"""
  __polar_doc = """Polar attributes of a Polar object"""
  polar = property(__getPolar, __putPolar, doc=__polar_doc)
  cart = property(__getCart, __putCart, doc=__cart_doc)

P1 = Polar(1.0, 180.0)
P2 = Polar(2.0, 90.0)

print("P1 = ", P1)
print("P2 = ", P2)

R2, T2 = P2.polar
X2, Y2 = P2.cart

print(R2, T2)
print(X2, Y2)

P1.cart = 0.0, -3.0
P2.cart = 4.0,  0.0

print("P1 = ", P1)
print("P2 = ", P2)

Expected result:

P1 =  r = 1.000000, theta = 3.141593
P2 =  r = 2.000000, theta = 1.570796
2.0 90.0
1.22464679915e-16 2.0
P1 =  r = 3.000000, theta = -1.570796
P2 =  r = 4.000000, theta = 0.000000

Solution

Step 5

Add a static method to compute the distance between two Polar points. Transform the file into a module that can be called externally.

class Polar(object):
  """The Polar Class that describes point2D object either in polar coordinates (r, theta), with theta in degrees, or in cartesian coordinates (x, y)"""

  def __init__(self, r, degree):
    """Polar constructor"""
    pass

  def __repr__(self):
    """Representation of a Polar object"""
    pass

  def __putPolar(self, (r, degree)):
    pass

  def __getPolar(self):
    pass

  def __getCart(self):
    pass

  def __putCart(self, (x, y) ):
    pass

  __cart_doc = """Cartesian attributes of a Polar object"""
  __polar_doc = """Polar attributes of a Polar object"""
  polar = property(__getPolar, __putPolar, doc=__polar_doc)
  cart = property(__getCart, __putCart, doc=__cart_doc)

  def distance(p1, p2):
    """Static method to compute the distance between two Polar object"""
    pass
  distance = staticmethod(distance)

if __name__ == "__main__":
  P1 = Polar(1.0, 180.0)
  P2 = Polar(2.0, 90.0)

  print("P1 = ", P1)
  print("P2 = ", P2)

  R2, T2 = P2.polar
  X2, Y2 = P2.cart

  print(R2, T2)
  print(X2, Y2)

  P1.cart = 0.0, -3.0
  P2.cart = 4.0,  0.0

  print("P1 = ", P1)
  print("P2 = ", P2)

  print(Polar.distance(P1, P2))

Expected result:

P1 =  r = 1.000000, theta = 3.141593
P2 =  r = 2.000000, theta = 1.570796
2.0 90.0
1.22464679915e-16 2.0
P1 =  r = 3.000000, theta = -1.570796
P2 =  r = 4.000000, theta = 0.000000
Distance between P1 and P2 =  5.0

Solution

Step 6

What about storing a Polar object using (internally) cartesian coordinates instead of polar coordinates. From a user point of view, it should change nothing. This is encapsulation :-).

A complete new implementation of a ''Polar'' module could be (with documentation and tests):

import math

__doc__ = """
This is the Polar module
"""
__author__ = "Gerald Monard"
__date__ = "2019-01-08"
__version__ = "0.1"


class Polar(object):
  """The Polar Class that describes point2D object either in polar coordinates (r, theta), with theta in degrees, or in cartesian coordinates (x, y)

>>> P1 = Polar(1.0, 180.0)
>>> P2 = Polar(2.0, 90.0)
>>> print "P1 = ", P1
P1 =  r = 1.000000, theta = 180.000000
>>> print "P2 = ", P2
P2 =  r = 2.000000, theta = 90.000000
>>> R2, T2 = P2.polar
>>> X2, Y2 = P2.cart
>>> print R2, T2
2.0 90.0
>>> print X2, Y2
1.22464679915e-16 2.0
>>> P1.cart = 0.0, -3.0
>>> P2.cart = 4.0,  0.0
>>> print "P1 = ", P1
P1 =  r = 3.000000, theta = -90.000000
>>> print "P2 = ", P2
P2 =  r = 4.000000, theta = 0.000000
>>> print Polar.distance(P1, P2)
5.0
"""

  __slots__ = ["_x", "_y"]
  def __init__(self, r, degree):
    """Polar constructor"""
    self._x, self._y = Polar._polar2cart(r, degree)

  def __repr__(self):
    """Representation of a Polar object"""
    return "r = %f, theta = %f" % self.polar

  def _getPolar(self):
    return Polar._cart2polar(self._x, self._y)

  def _putPolar(self, r, degree):
    self._x, self._y = Polar._polar2cart(r, degree)

  def _getCart(self):
    return self._x, self._y

  def _putCart(self, t):
    self._x = t[0]
    self._y = t[1]

  _cart_doc = """Cartesian attributes of a Polar object"""
  _polar_doc = """Polar attributes of a Polar object"""
  polar = property(_getPolar, _putPolar, doc=_polar_doc)
  cart = property(_getCart, _putCart, doc=_cart_doc)

  def distance(p1, p2):
    """Static method to compute the distance between two Polar object"""
    x1, y1 = p1.cart
    x2, y2 = p2.cart
    d = (x1-x2)**2 + (y1-y2)**2
    return math.sqrt(d)
  distance = staticmethod(distance)

  # some functions to help conversions...
  def _deg2rad(degree):
    return degree/180.0*math.pi
  _deg2rad = staticmethod(_deg2rad)

  def _rad2deg(radian):
    return radian*180.0/math.pi
  _rad2deg = staticmethod(_rad2deg)

  def _cart2polar(x, y):
    r = math.sqrt(x*x + y*y)
    theta = math.atan2(y,x)
    return r, Polar._rad2deg(theta)
  _cart2polar = staticmethod(_cart2polar)

  def _polar2cart(r, degree):
    theta = Polar._deg2rad(degree)
    x = r * math.cos(theta)
    y = r * math.sin(theta)
    return x, y
  _polar2cart = staticmethod(_polar2cart)

if __name__ == "__main__":
  import doctest
  doctest.testmod()

Using the Polar module:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python3

from Polar import Polar


P1 = Polar(1.0, 180.0)
P2 = Polar(2.0, 90.0)

print "P1 = ", P1
print "P2 = ", P2

R2, T2 = P2.polar
X2, Y2 = P2.cart

print R2, T2
print X2, Y2

P1.cart = 0.0, -3.0
P2.cart = 4.0,  0.0

print "P1 = ", P1
print "P2 = ", P2

print "Distance between P1 and P2 = ", Polar.distance(P1, P2)

Pydoc output:

Help on module Polar:

NAME
    Polar - This is the Polar module

FILE
    Polar.py

CLASSES
    __builtin__.object
        Polar

    class Polar(__builtin__.object)
     |  The Polar Class that describes point2D object either in polar coordinates (r, theta), with theta in degrees, or in cartesian coordinates (x, y)
     |
     |  >>> P1 = Polar(1.0, 180.0)
     |  >>> P2 = Polar(2.0, 90.0)
     |  >>> print "P1 = ", P1
     |  P1 =  r = 1.000000, theta = 180.000000
     |  >>> print "P2 = ", P2
     |  P2 =  r = 2.000000, theta = 90.000000
     |  >>> R2, T2 = P2.polar
     |  >>> X2, Y2 = P2.cart
     |  >>> print R2, T2
     |  2.0 90.0
     |  >>> print X2, Y2
     |  1.22464679915e-16 2.0
     |  >>> P1.cart = 0.0, -3.0
     |  >>> P2.cart = 4.0,  0.0
     |  >>> print "P1 = ", P1
     |  P1 =  r = 3.000000, theta = -90.000000
     |  >>> print "P2 = ", P2
     |  P2 =  r = 4.000000, theta = 0.000000
     |  >>> print Polar.distance(P1, P2)
     |  5.0
     |
     |  Methods defined here:
     |
     |  __init__(self, r, degree)
     |      Polar constructor
     |
     |  __repr__(self)
     |      Representation of a Polar object
     |
     |  ----------------------------------------------------------------------
     |  Static methods defined here:
     |
     |  distance(p1, p2)
     |      Static method to compute the distance between two Polar object
     |
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |
     |  cart
     |      Cartesian attributes of a Polar object
     |
     |  polar
     |      Polar attributes of a Polar object

DATA
    __author__ = 'Gerald Monard'
    __date__ = '2019-01-08'
    __version__ = '0.1'

VERSION
    0.1

DATE
    2019-01-08

AUTHOR
    Gerald Monard

Solution